Add sleep record (only supports a single record atm though)

main
Mari 3 years ago
parent 548f6766e4
commit b1f03c0cf4
  1. 5
      .gulp.json
  2. 0
      gulpfile.cjs
  3. 147
      package-lock.json
  4. 4
      package.json
  5. 31
      src/commands/AddEntry.ts
  6. 14
      src/commands/UpdateEmpathyGuide.ts
  7. 2
      src/datatypes/Condition.ts
  8. 2
      src/datatypes/EmpathyGroup.ts
  9. 2
      src/datatypes/EmpathyGroupList.ts
  10. 4
      src/datatypes/EmpathyGuide.ts
  11. 17
      src/datatypes/Entry.ts
  12. 6
      src/datatypes/GuidedEmpathy.ts
  13. 4
      src/datatypes/Journal.ts
  14. 2
      src/datatypes/Persona.ts
  15. 96
      src/datatypes/SleepRecord.ts
  16. 2
      src/datatypes/Suicidality.ts
  17. 12
      src/index.ts
  18. 6
      src/prompts/Inquire.ts
  19. 14
      src/prompts/implementations/ConditionPrompt.ts
  20. 8
      src/prompts/implementations/EmpathyGroupListPrompt.ts
  21. 6
      src/prompts/implementations/EmpathyGroupPrompt.ts
  22. 16
      src/prompts/implementations/EmpathyGuidePrompt.ts
  23. 13
      src/prompts/implementations/EntryMainMenuPrompt.ts
  24. 10
      src/prompts/implementations/EntryPrompt.ts
  25. 14
      src/prompts/implementations/GuidedEmpathyListPrompt.ts
  26. 14
      src/prompts/implementations/GuidedEmpathyPrompt.ts
  27. 8
      src/prompts/implementations/JournalEntryPrompt.ts
  28. 196
      src/prompts/implementations/SleepRecordPrompt.ts
  29. 8
      src/prompts/implementations/SuicidalityPrompt.ts
  30. 8
      src/prompts/implementations/SummaryPrompt.ts
  31. 386
      src/prompts/types/DateInput.ts
  32. 19
      src/prompts/types/HierarchicalCheckboxInput.ts
  33. 8
      src/prompts/types/MultiTextInput.ts
  34. 8
      src/prompts/types/index.ts
  35. 8
      src/repository/LocalRepository.ts
  36. 2
      src/schemata/SchemaData.ts
  37. 4
      src/schemata/YAMLPrompt.ts
  38. 2
      tsconfig.json

@ -0,0 +1,5 @@
{
"flags": {
"gulpfile": "gulpfile.cjs"
}
}

147
package-lock.json generated

@ -5,17 +5,20 @@
"requires": true,
"packages": {
"": {
"name": "mari-guided-journal",
"version": "1.0.0",
"license": "ISC",
"dependencies": {
"ajv": "^8.6.2",
"capitalize": "^2.0.3",
"chalk": "^2.4.2",
"cli-cursor": "^4.0.0",
"env-paths": "^2.2.1",
"figures": "^3.2.0",
"fuzzy": "^0.1.3",
"inquirer": "^8.1.2",
"js-yaml": "^4.1.0",
"luxon": "^2.0.2",
"make-dir": "^2.1.0",
"pluralize": "^8.0.0",
"rxjs": "^6.6.7",
@ -30,6 +33,7 @@
"@types/concurrently": "^5.2.1",
"@types/inquirer": "^7.3.3",
"@types/js-yaml": "^4.0.2",
"@types/luxon": "^2.0.4",
"@types/node-fetch": "^2.5.4",
"@types/pluralize": "^0.0.29",
"@types/yargs": "^17.0.0",
@ -1664,6 +1668,12 @@
"integrity": "sha512-KbeHS/Y4R+k+5sWXEYzAZKuB1yQlZtEghuhRxrVRLaqhtoG5+26JwQsa4HyS3AWX8v1Uwukma5HheduUDskasA==",
"dev": true
},
"node_modules/@types/luxon": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-2.0.4.tgz",
"integrity": "sha512-l3xuhmyF2kBldy15SeY6d6HbK2BacEcSK1qTF1ISPtPHr29JH0C1fndz9ExXLKpGl0J6pZi+dGp1i5xesMt60Q==",
"dev": true
},
"node_modules/@types/node": {
"version": "16.4.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.13.tgz",
@ -2784,14 +2794,17 @@
}
},
"node_modules/cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
"integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
"dependencies": {
"restore-cursor": "^3.1.0"
"restore-cursor": "^4.0.0"
},
"engines": {
"node": ">=8"
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/cli-spinners": {
@ -4735,6 +4748,17 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/inquirer/node_modules/cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"dependencies": {
"restore-cursor": "^3.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/inquirer/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -4759,6 +4783,18 @@
"node": ">=8"
}
},
"node_modules/inquirer/node_modules/restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"dependencies": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/inquirer/node_modules/rxjs": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz",
@ -5464,6 +5500,14 @@
"node": ">=0.10.0"
}
},
"node_modules/luxon": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-2.0.2.tgz",
"integrity": "sha512-ZRioYLCgRHrtTORaZX1mx+jtxKtKuI5ZDvHNAmqpUzGqSrR+tL4FVLn/CUGMA3h0+AKD1MAxGI5GnCqR5txNqg==",
"engines": {
"node": "*"
}
},
"node_modules/make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@ -6174,6 +6218,17 @@
"url": "https://github.com/chalk/chalk?sponsor=1"
}
},
"node_modules/ora/node_modules/cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"dependencies": {
"restore-cursor": "^3.1.0"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ora/node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -6198,6 +6253,18 @@
"node": ">=8"
}
},
"node_modules/ora/node_modules/restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"dependencies": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
},
"engines": {
"node": ">=8"
}
},
"node_modules/ora/node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@ -7026,15 +7093,18 @@
}
},
"node_modules/restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
"integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
"dependencies": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
},
"engines": {
"node": ">=8"
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/ret": {
@ -9558,6 +9628,12 @@
"integrity": "sha512-KbeHS/Y4R+k+5sWXEYzAZKuB1yQlZtEghuhRxrVRLaqhtoG5+26JwQsa4HyS3AWX8v1Uwukma5HheduUDskasA==",
"dev": true
},
"@types/luxon": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-2.0.4.tgz",
"integrity": "sha512-l3xuhmyF2kBldy15SeY6d6HbK2BacEcSK1qTF1ISPtPHr29JH0C1fndz9ExXLKpGl0J6pZi+dGp1i5xesMt60Q==",
"dev": true
},
"@types/node": {
"version": "16.4.13",
"resolved": "https://registry.npmjs.org/@types/node/-/node-16.4.13.tgz",
@ -10425,11 +10501,11 @@
"dev": true
},
"cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
"integrity": "sha512-VGtlMu3x/4DOtIUwEkRezxUZ2lBacNJCHash0N0WeZDBS+7Ux1dm3XWAgWYxLJFMMdOeXMHXorshEFhbMSGelg==",
"requires": {
"restore-cursor": "^3.1.0"
"restore-cursor": "^4.0.0"
}
},
"cli-spinners": {
@ -11992,6 +12068,14 @@
"supports-color": "^7.1.0"
}
},
"cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"requires": {
"restore-cursor": "^3.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -12010,6 +12094,15 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"requires": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
}
},
"rxjs": {
"version": "7.3.0",
"resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.3.0.tgz",
@ -12550,6 +12643,11 @@
"integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==",
"dev": true
},
"luxon": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/luxon/-/luxon-2.0.2.tgz",
"integrity": "sha512-ZRioYLCgRHrtTORaZX1mx+jtxKtKuI5ZDvHNAmqpUzGqSrR+tL4FVLn/CUGMA3h0+AKD1MAxGI5GnCqR5txNqg=="
},
"make-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz",
@ -13101,6 +13199,14 @@
"supports-color": "^7.1.0"
}
},
"cli-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
"integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==",
"requires": {
"restore-cursor": "^3.1.0"
}
},
"color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@ -13119,6 +13225,15 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
"integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="
},
"restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"requires": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"
}
},
"supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@ -13773,9 +13888,9 @@
}
},
"restore-cursor": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
"integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==",
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-4.0.0.tgz",
"integrity": "sha512-I9fPXU9geO9bHOt9pHHOhOkYerIMsmVaWB0rA2AI9ERh/+x/i7MV5HKBNrg+ljO5eoPVgCcnFuRjJ9uH6I/3eg==",
"requires": {
"onetime": "^5.1.0",
"signal-exit": "^3.0.2"

@ -12,6 +12,7 @@
"prestart": "npm run --silent build",
"debug": "npm run --silent start --inspect"
},
"type": "module",
"keywords": [],
"author": "Mari",
"license": "ISC",
@ -19,11 +20,13 @@
"ajv": "^8.6.2",
"capitalize": "^2.0.3",
"chalk": "^2.4.2",
"cli-cursor": "^4.0.0",
"env-paths": "^2.2.1",
"figures": "^3.2.0",
"fuzzy": "^0.1.3",
"inquirer": "^8.1.2",
"js-yaml": "^4.1.0",
"luxon": "^2.0.2",
"make-dir": "^2.1.0",
"pluralize": "^8.0.0",
"rxjs": "^6.6.7",
@ -38,6 +41,7 @@
"@types/concurrently": "^5.2.1",
"@types/inquirer": "^7.3.3",
"@types/js-yaml": "^4.0.2",
"@types/luxon": "^2.0.4",
"@types/node-fetch": "^2.5.4",
"@types/pluralize": "^0.0.29",
"@types/yargs": "^17.0.0",

@ -1,17 +1,18 @@
import {Separator} from "inquirer";
import {inquire} from "../prompts/Inquire";
import {summaryPrompt} from "../prompts/implementations/SummaryPrompt";
import {journalEntryPrompt} from "../prompts/implementations/JournalEntryPrompt";
import {guidedEmpathyListPrompt} from "../prompts/implementations/GuidedEmpathyListPrompt";
import {entryMainMenuPrompt} from "../prompts/implementations/EntryMainMenuPrompt";
import {Separator} from "../prompts/Inquire.js";
import {inquire} from "../prompts/Inquire.js";
import {summaryPrompt} from "../prompts/implementations/SummaryPrompt.js";
import {journalEntryPrompt} from "../prompts/implementations/JournalEntryPrompt.js";
import {guidedEmpathyListPrompt} from "../prompts/implementations/GuidedEmpathyListPrompt.js";
import {entryMainMenuPrompt} from "../prompts/implementations/EntryMainMenuPrompt.js";
import {CommandModule} from "yargs";
import { registerPrompts } from "../prompts/types";
import {LocalRepository} from "../repository/LocalRepository";
import {conditionPrompt} from "../prompts/implementations/ConditionPrompt";
import {entryPrompt} from "../prompts/implementations/EntryPrompt";
import {guidedEmpathyPrompt} from "../prompts/implementations/GuidedEmpathyPrompt";
import {suicidalityPrompt} from "../prompts/implementations/SuicidalityPrompt";
import {yamlPrompt} from "../schemata/YAMLPrompt";
import { registerPrompts } from "../prompts/types/index.js";
import {LocalRepository} from "../repository/LocalRepository.js";
import {conditionPrompt} from "../prompts/implementations/ConditionPrompt.js";
import {entryPrompt} from "../prompts/implementations/EntryPrompt.js";
import {guidedEmpathyPrompt} from "../prompts/implementations/GuidedEmpathyPrompt.js";
import {suicidalityPrompt} from "../prompts/implementations/SuicidalityPrompt.js";
import {yamlPrompt} from "../schemata/YAMLPrompt.js";
import {sleepRecordPrompt} from "../prompts/implementations/SleepRecordPrompt.js";
export function addEntryCommand(): CommandModule {
return {
@ -26,6 +27,7 @@ export function addEntryCommand(): CommandModule {
const summary = summaryPrompt({inquire})
const journal = journalEntryPrompt({inquire})
const suicidality = suicidalityPrompt({inquire})
const sleep = sleepRecordPrompt({inquire})
const empathy = guidedEmpathyPrompt({
inquire,
guideFactory: () => empathyGuide,
@ -45,6 +47,7 @@ export function addEntryCommand(): CommandModule {
new Separator(),
journal.mainMenu,
empathyList.mainMenu,
sleep.mainMenu,
// TODO: Modified HierarchicalCheckboxInput to allow for putting more things in the main menu than
// there are letters
// TODO: gratitude journaling
@ -54,7 +57,7 @@ export function addEntryCommand(): CommandModule {
/*
{
name: typeof entry.needs === "object" ? "Check back in on needs" : "Check in on needs",
// TODO: physical needs: food, water, sleep, exercise tracking, possibly with autocompletes for each saved in a file like the empathy guide
// TODO: physical needs: food, water, exercise tracking, possibly with autocompletes for each saved in a file like the empathy guide
value: NEEDS,
key: "n"
},

@ -1,11 +1,11 @@
import {CommandModule} from "yargs";
import {inquire} from "../prompts/Inquire";
import {registerPrompts} from "../prompts/types";
import {LocalRepository} from "../repository/LocalRepository";
import {empathyGroupPrompt} from "../prompts/implementations/EmpathyGroupPrompt";
import {empathyGroupListPrompt} from "../prompts/implementations/EmpathyGroupListPrompt";
import {empathyGuidePrompt} from "../prompts/implementations/EmpathyGuidePrompt";
import {yamlPrompt} from "../schemata/YAMLPrompt";
import {inquire} from "../prompts/Inquire.js";
import {registerPrompts} from "../prompts/types/index.js";
import {LocalRepository} from "../repository/LocalRepository.js";
import {empathyGroupPrompt} from "../prompts/implementations/EmpathyGroupPrompt.js";
import {empathyGroupListPrompt} from "../prompts/implementations/EmpathyGroupListPrompt.js";
import {empathyGuidePrompt} from "../prompts/implementations/EmpathyGuidePrompt.js";
import {yamlPrompt} from "../schemata/YAMLPrompt.js";
export function updateEmpathyGuideCommand(): CommandModule {
return {

@ -1,4 +1,4 @@
import {schema} from "../schemata/SchemaData";
import {schema} from "../schemata/SchemaData.js";
export enum Condition {
CRITICAL = "Critical",

@ -1,4 +1,4 @@
import {schema} from "../schemata/SchemaData";
import {schema} from "../schemata/SchemaData.js";
export interface EmpathyGroup {
readonly header: string

@ -1,4 +1,4 @@
import {EmpathyGroup} from "./EmpathyGroup";
import {EmpathyGroup} from "./EmpathyGroup.js";
export function totalItems(groups: readonly EmpathyGroup[]): number {
return groups.map((group) => group.items.length).reduce((a, b) => a + b)

@ -1,5 +1,5 @@
import {schema} from "../schemata/SchemaData";
import {EmpathyGroup, EmpathyGroupJTD} from "./EmpathyGroup";
import {schema} from "../schemata/SchemaData.js";
import {EmpathyGroup, EmpathyGroupJTD} from "./EmpathyGroup.js";
export interface EmpathyGuide {
readonly feelings?: {

@ -1,15 +1,17 @@
import {Condition, ConditionJTD} from "./Condition";
import {GuidedEmpathy, GuidedEmpathyJTD} from "./GuidedEmpathy";
import {Suicidality, SuicidalityJTD} from "./Suicidality";
import {schema} from "../schemata/SchemaData";
import {Condition, ConditionJTD} from "./Condition.js";
import {GuidedEmpathy, GuidedEmpathyJTD} from "./GuidedEmpathy.js";
import {Suicidality, SuicidalityJTD} from "./Suicidality.js";
import {schema} from "../schemata/SchemaData.js";
import {SleepRecord, SleepRecordListJTD} from "./SleepRecord.js";
export interface Entry {
readonly startedAt: Date
readonly finishedAt: Date
readonly condition: Condition
readonly condition?: Condition
readonly summary?: string
readonly journalEntry?: string
readonly guidedEmpathy?: readonly GuidedEmpathy[]
readonly sleepRecords?: readonly SleepRecord[]
// readonly needs?: Needs
// readonly music?: readonly string[]
// readonly rpg?: RPGStats
@ -24,17 +26,18 @@ export const EntryJTD = schema({
properties: {
startedAt: { type: "timestamp" },
finishedAt: { type: "timestamp" },
condition: ConditionJTD.reference,
},
optionalProperties: {
condition: ConditionJTD.reference,
summary: { type: "string" },
journalEntry: { type: "string" },
guidedEmpathy: { elements: GuidedEmpathyJTD.reference },
suicidality: SuicidalityJTD.reference,
sleepRecords: SleepRecordListJTD.reference,
}
},
typeHint: null as Entry|null,
key: "entry",
references: [ConditionJTD, GuidedEmpathyJTD, SuicidalityJTD]
references: [ConditionJTD, GuidedEmpathyJTD, SuicidalityJTD, SleepRecordListJTD, ...SleepRecordListJTD.requiredReferences, ...GuidedEmpathyJTD.requiredReferences]
})

@ -1,4 +1,4 @@
import {isPopulatedArray} from "../utils/Arrays";
import {isPopulatedArray} from "../utils/Arrays.js";
import {
Persona,
personaName,
@ -6,9 +6,9 @@ import {
personaPronounObject,
personaPronounPossessive,
personaVerb
} from "./Persona";
} from "./Persona.js";
import chalk from "chalk";
import {schema} from "../schemata/SchemaData";
import {schema} from "../schemata/SchemaData.js";
import capitalize from "capitalize";
export interface GuidedEmpathy {

@ -1,5 +1,5 @@
import {Entry, EntryJTD} from "./Entry";
import {schema} from "../schemata/SchemaData";
import {Entry, EntryJTD} from "./Entry.js";
import {schema} from "../schemata/SchemaData.js";
/* export interface PersonaState {
readonly persona: Persona

@ -1,4 +1,4 @@
import {schema} from "../schemata/SchemaData";
import {schema} from "../schemata/SchemaData.js";
export enum Persona {
HEART = "Heart",

@ -0,0 +1,96 @@
import {schema} from "../schemata/SchemaData.js";
export enum SleepQuality {
ACIDIC = "Acidic",
RESTLESS = "Restless",
NIGHTMARISH = "Nightmarish",
TIMESKIP = "Timeskip",
DREAMLESS = "Dreamless",
DREAMY = "Dreamy",
ECSTASY = "Ecstasy",
}
export const SLEEP_QUALITIES: SleepQuality[] = [SleepQuality.ACIDIC, SleepQuality.RESTLESS, SleepQuality.NIGHTMARISH, SleepQuality.TIMESKIP, SleepQuality.DREAMLESS, SleepQuality.DREAMY, SleepQuality.ECSTASY]
export type SleepQualityJTD = typeof SleepQualityJTD
export const SleepQualityJTD = schema({
schema: {
enum: SLEEP_QUALITIES
},
key: "sleepQuality",
references: [],
})
export enum WakeQuality {
AGONIZED = "Agonized",
EXHAUSTED = "Exhausted",
DROWSY = "Drowsy",
RESTED = "Rested",
ENERGIZED = "Energized",
}
export const WAKE_QUALITIES: WakeQuality[] = [WakeQuality.AGONIZED, WakeQuality.EXHAUSTED, WakeQuality.DROWSY, WakeQuality.RESTED, WakeQuality.ENERGIZED]
export type WakeQualityJTD = typeof WakeQualityJTD
export const WakeQualityJTD = schema({
schema: {
enum: WAKE_QUALITIES
},
key: "wakeQuality",
references: [],
})
export interface SleepRecord {
readonly sleepAt?: Date
readonly sleepQuality?: SleepQuality
readonly interruptions?: number
readonly wakeAt?: Date
readonly wakeQuality?: WakeQuality
readonly dreams?: string
}
export type SleepRecordJTD = typeof SleepRecordJTD
export const SleepRecordJTD = schema({
schema: {
optionalProperties: {
sleepAt: { type: "timestamp" },
sleepQuality: SleepQualityJTD.reference,
interruptions: { type: "uint32" },
wakeAt: { type: "timestamp" },
wakeQuality: WakeQualityJTD.reference,
dreams: { type: "string" },
}
},
typeHint: null as SleepRecord|null,
key: "sleepRecord",
references: [SleepQualityJTD, WakeQualityJTD],
})
export type SleepRecordListJTD = typeof SleepRecordListJTD
export const SleepRecordListJTD = schema({
schema: {
elements: SleepRecordJTD.reference
},
typeHint: null as (readonly SleepRecord[])|null,
key: "sleepRecords",
references: [SleepRecordJTD, ...SleepRecordJTD.requiredReferences],
})
const TimeFormat = Intl.DateTimeFormat([], {
dateStyle: undefined,
timeStyle: undefined,
year: undefined,
month: undefined,
day: undefined,
weekday: undefined,
hour: "numeric",
minute: "2-digit",
second: undefined,
fractionalSecondDigits: undefined,
timeZone: undefined,
hour12: true,
hourCycle: "h12",
})
export function sleepRecordToString(record: SleepRecord) {
const sleepAt = record.sleepAt === undefined ? null : TimeFormat.format(record.sleepAt)
const wakeAt = record.wakeAt === undefined ? null : TimeFormat.format(record.wakeAt)
return `${sleepAt ?? "?:??"} (${record.sleepQuality ?? "???"}) -${record.interruptions ?? "?"}-> ${wakeAt ?? "?:??"} (${record.wakeQuality ?? "???"})${typeof record.dreams === "string" ? " with dream journal" : ""}`
}

@ -1,4 +1,4 @@
import {schema} from "../schemata/SchemaData";
import {schema} from "../schemata/SchemaData.js";
export enum Suicidality {
NONE = "None",

@ -1,13 +1,15 @@
import {addEntryCommand} from "./commands/AddEntry";
import {updateEmpathyGuideCommand} from "./commands/UpdateEmpathyGuide";
import yargs from "yargs"
import {addEntryCommand} from "./commands/AddEntry.js";
import {updateEmpathyGuideCommand} from "./commands/UpdateEmpathyGuide.js";
import {Argv, default as yargs} from "yargs"
yargs
const Yargs = yargs as unknown as {(): Argv}
Yargs()
.scriptName("mari-status-bar")
.command(addEntryCommand())
.command(updateEmpathyGuideCommand())
.demandCommand()
.parseAsync()
.catch((err) => {
.catch((err: unknown) => {
console.error(err)
})

@ -1,5 +1,9 @@
import {prompt, QuestionMap} from "inquirer";
import {QuestionMap} from "inquirer";
import inquirer from "inquirer";
export const prompt = inquirer.prompt;
export const registerPrompt = inquirer.registerPrompt;
export const Separator = inquirer.Separator;
export type InquireFunction<QuestionT extends QuestionMap[keyof QuestionMap], AnswerT extends QuestionT["default"]> = (question: QuestionT) => Promise<AnswerT>
export type ShowFunction = (text: string) => Promise<void>

@ -1,10 +1,10 @@
import {ListQuestion, Separator} from "inquirer";
import {personaPossessive, PersonaPrompt} from "../../datatypes/Persona";
import {ListQuestion} from "inquirer";
import {personaPossessive, PersonaPrompt} from "../../datatypes/Persona.js";
import chalk from "chalk";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt";
import {asDefault, identity} from "../../utils/Objects";
import {InquireFunction} from "../Inquire";
import {Condition} from "../../datatypes/Condition";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt.js";
import {asDefault, identity} from "../../utils/Objects.js";
import {InquireFunction, Separator} from "../Inquire.js";
import {Condition} from "../../datatypes/Condition.js";
export interface ConditionPromptOptions extends Partial<Omit<ListQuestion, "name" | "type" | "choices">>, PersonaPrompt {
}
@ -19,7 +19,7 @@ export function conditionPrompt(deps: ConditionPromptDependencies): {
} {
return makeEntryMainMenuChoice({
property: "condition",
name: (input) => `Change condition ${chalk.dim(`(currently ${chalk.greenBright(input)})`)}`,
name: (input) => typeof input === "string" ? `Change condition ${chalk.dim(`(currently ${chalk.greenBright(input)})`)}` : `Add condition`,
key: "c",
injected: (options) => promptForCondition(options, deps),
toOptions: asDefault,

@ -1,9 +1,9 @@
import {EmpathyGroup} from "../../datatypes/EmpathyGroup";
import {isPopulatedArray} from "../../utils/Arrays";
import {EmpathyGroup} from "../../datatypes/EmpathyGroup.js";
import {isPopulatedArray} from "../../utils/Arrays.js";
import chalk from "chalk";
import pluralize from "pluralize";
import {InquireFunction} from "../Inquire";
import {EmpathyGroupPromptOptions} from "./EmpathyGroupPrompt";
import {InquireFunction} from "../Inquire.js";
import {EmpathyGroupPromptOptions} from "./EmpathyGroupPrompt.js";
import {ListQuestion} from "inquirer";
export interface EmpathyGroupListPromptOptions {

@ -1,8 +1,8 @@
import capitalize from "capitalize";
import {YamlPromptFunction} from "../../schemata/YAMLPrompt";
import {YamlPromptFunction} from "../../schemata/YAMLPrompt.js";
import chalk from "chalk";
import {InquireFunction} from "../Inquire";
import {EmpathyGroup, EmpathyGroupJTD} from "../../datatypes/EmpathyGroup";
import {InquireFunction} from "../Inquire.js";
import {EmpathyGroup, EmpathyGroupJTD} from "../../datatypes/EmpathyGroup.js";
import {EditorQuestion, ExpandQuestion, InputQuestion, MultiTextInputQuestion} from "inquirer";
export interface EmpathyGroupPromptOptions {

@ -1,13 +1,13 @@
import {isPopulatedArray} from "../../utils/Arrays";
import {isPopulatedArray} from "../../utils/Arrays.js";
import chalk from "chalk";
import pluralize from "pluralize";
import {totalItems} from "../../datatypes/EmpathyGroupList";
import {ExpandQuestion, Separator} from "inquirer";
import {YamlPromptFunction} from "../../schemata/YAMLPrompt";
import {InquireFunction} from "../Inquire";
import {EmpathyGroupListPromptOptions} from "./EmpathyGroupListPrompt";
import {EmpathyGroup} from "../../datatypes/EmpathyGroup";
import {EmpathyGuide, EmpathyGuideJTD} from "../../datatypes/EmpathyGuide";
import {totalItems} from "../../datatypes/EmpathyGroupList.js";
import {ExpandQuestion} from "inquirer";
import {YamlPromptFunction} from "../../schemata/YAMLPrompt.js";
import {InquireFunction, Separator} from "../Inquire.js";
import {EmpathyGroupListPromptOptions} from "./EmpathyGroupListPrompt.js";
import {EmpathyGroup} from "../../datatypes/EmpathyGroup.js";
import {EmpathyGuide, EmpathyGuideJTD} from "../../datatypes/EmpathyGuide.js";
export interface EmpathyGuidePromptOptions {
readonly default?: EmpathyGuide

@ -1,8 +1,11 @@
import {InquireFunction} from "../Inquire";
import {ExpandChoiceOptions, ExpandQuestion, Separator, SeparatorOptions} from "inquirer";
import {merge} from "../../utils/Merge";
import {Entry, EntryJTD} from "../../datatypes/Entry";
import {YamlPromptFunction} from "../../schemata/YAMLPrompt";
import {InquireFunction} from "../Inquire.js";
import {ExpandChoiceOptions, ExpandQuestion, SeparatorOptions} from "inquirer";
import inquirer from "inquirer";
import {merge} from "../../utils/Merge.js";
import {Entry, EntryJTD} from "../../datatypes/Entry.js";
import {YamlPromptFunction} from "../../schemata/YAMLPrompt.js";
const Separator = inquirer.Separator
const VIEW = ".View"
const DONE = ".Done"

@ -1,8 +1,8 @@
import {ConditionPromptOptions} from "./ConditionPrompt";
import {Condition} from "../../datatypes/Condition";
import {SummaryPromptOptions} from "./SummaryPrompt";
import {EntryMainMenuOptions} from "./EntryMainMenuPrompt";
import {Entry} from "../../datatypes/Entry";
import {ConditionPromptOptions} from "./ConditionPrompt.js";
import {Condition} from "../../datatypes/Condition.js";
import {SummaryPromptOptions} from "./SummaryPrompt.js";
import {EntryMainMenuOptions} from "./EntryMainMenuPrompt.js";
import {Entry} from "../../datatypes/Entry.js";
interface EntryPromptOptions {
}

@ -1,13 +1,13 @@
import {PersonaPrompt} from "../../datatypes/Persona";
import {guidedEmpathyToStringShort, GuidedEmpathy} from "../../datatypes/GuidedEmpathy";
import {InquireFunction} from "../Inquire";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt";
import {PersonaPrompt} from "../../datatypes/Persona.js";
import {guidedEmpathyToStringShort, GuidedEmpathy} from "../../datatypes/GuidedEmpathy.js";
import {InquireFunction} from "../Inquire.js";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt.js";
import chalk from "chalk";
import pluralize from "pluralize";
import {isPopulatedArray} from "../../utils/Arrays";
import {isPopulatedArray} from "../../utils/Arrays.js";
import {ListChoiceOptions, ListQuestion} from "inquirer";
import {asDefault} from "../../utils/Objects";
import {GuidedEmpathyPromptOptions} from "./GuidedEmpathyPrompt";
import {asDefault} from "../../utils/Objects.js";
import {GuidedEmpathyPromptOptions} from "./GuidedEmpathyPrompt.js";
export interface GuidedEmpathyListPromptOptions extends PersonaPrompt {
readonly default?: readonly GuidedEmpathy[]

@ -4,13 +4,13 @@ import {
personaPronounObject,
personaPronounPossessive,
personaVerb
} from "../../datatypes/Persona";
import {InquireFunction, ShowFunction} from "../Inquire";
import {EmpathyGuide} from "../../datatypes/EmpathyGuide";
import {isPopulatedArray} from "../../utils/Arrays";
import Separator from "inquirer/lib/objects/separator";
import {EmpathyGroup} from "../../datatypes/EmpathyGroup";
import {GuidedEmpathy, guidedEmpathyToString, isPopulatedGuidedEmpathy} from "../../datatypes/GuidedEmpathy";
} from "../../datatypes/Persona.js";
import {InquireFunction, ShowFunction} from "../Inquire.js";
import {EmpathyGuide} from "../../datatypes/EmpathyGuide.js";
import {isPopulatedArray} from "../../utils/Arrays.js";
import {Separator} from "../Inquire.js";
import {EmpathyGroup} from "../../datatypes/EmpathyGroup.js";
import {GuidedEmpathy, guidedEmpathyToString, isPopulatedGuidedEmpathy} from "../../datatypes/GuidedEmpathy.js";
import {HierarchicalCheckboxChildChoice, HierarchicalCheckboxParentChoice, HierarchicalCheckboxQuestion, MultiTextInputQuestion } from "inquirer";
export interface GuidedEmpathyPromptOptions extends PersonaPrompt {

@ -1,11 +1,11 @@
import {EditorQuestion} from "inquirer";
import {InquireFunction} from "../Inquire";
import {personaPossessive, PersonaPrompt} from "../../datatypes/Persona";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt";
import {InquireFunction} from "../Inquire.js";
import {personaPossessive, PersonaPrompt} from "../../datatypes/Persona.js";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt.js";
import chalk from "chalk";
import pluralize from "pluralize";
import wordcount from "wordcount";
import {asDefault} from "../../utils/Objects";
import {asDefault} from "../../utils/Objects.js";
export interface JournalEntryPromptOptions extends Partial<Omit<EditorQuestion, "name" | "type">>, PersonaPrompt {}

@ -0,0 +1,196 @@
import {
DateQuestion,
EditorQuestion,
ListQuestion,
NumberQuestion,
} from "inquirer";
import {Separator} from "../Inquire.js";
import {InquireFunction} from "../Inquire.js";
import {SleepQuality, SleepRecord, sleepRecordToString, WakeQuality} from "../../datatypes/SleepRecord.js";
import {DateTime} from "luxon";
import chalk from "chalk";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt.js";
export interface SleepRecordPromptOptions extends Partial<Omit<EditorQuestion, "name"|"type"|"default"> & Omit<DateQuestion, "name"|"type"|"default"> & Omit<ListQuestion, "name"|"type"|"default"> & Omit<NumberQuestion, "name"|"type"|"default">>{
default?: SleepRecord,
}
export interface SleepRecordPromptDependencies {
readonly inquire: InquireFunction<EditorQuestion, string> & InquireFunction<DateQuestion, Date|null> & InquireFunction<ListQuestion & {default: SleepQuality|undefined}, SleepQuality|undefined> & InquireFunction<ListQuestion & {default: WakeQuality|undefined}, WakeQuality|undefined> & InquireFunction<NumberQuestion, number>
}
export function sleepRecordPrompt(deps: SleepRecordPromptDependencies): {
(options: SleepRecordPromptOptions): Promise<SleepRecord|null>,
mainMenu: EntryMainMenuChoice<"sleepRecords">,
} {
return makeEntryMainMenuChoice({
property: "sleepRecords",
name: (input) => typeof input !== "object" || input.length === 0 ? `Add sleep record` : `Change sleep record ${chalk.dim(`(currently ${chalk.greenBright(sleepRecordToString(input[0]))})`)}`,
key: "z",
injected: (options) => promptForSleepRecord(options, deps),
toOptions: (value) => typeof value !== "object" || value.length === 0 ? {} : {default: value[0]},
toProperty: (value) => value === null ? undefined : [value],
})
}
export async function promptForSleepRecord(options: SleepRecordPromptOptions, {inquire}: SleepRecordPromptDependencies): Promise<SleepRecord | null> {
const oldBedTime = options.default?.sleepAt
const newBedTime = await inquire({
...options,
type: "date",
message: "About when did you get to sleep?",
clearable: true,
startCleared: false,
default: oldBedTime ?? DateTime.local().set({hour: 23, minute: 0, second: 0, millisecond: 0}).minus({days: 1}).toJSDate(),
format: {
year: undefined,
weekday: "short",
month: "short",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: undefined,
fractionalSecondDigits: undefined,
timeZoneName: "short",
},
startingDatePart: "hour",
deltas: {
weekday: 1,
hour: [1, 5, 10],
minute: [15, 5, 1],
default: 0
}
})
const oldSleepQuality = options.default?.sleepQuality
const newSleepQuality = await inquire({
...options,
type: "list",
message: `How'd you sleep?`,
choices: [
{
value: SleepQuality.ACIDIC,
name: `Acidic ${chalk.dim("(extremely poor/extremely restless/heavy acid reflux)")}`
},
{
value: SleepQuality.RESTLESS,
name: `Restless ${chalk.dim("(very poor/very restless/fever dreamy)")}`
},
{
value: SleepQuality.NIGHTMARISH,
name: `Nightmarish ${chalk.dim("(poor/restless/plagued with nightmares)")}`
},
{
value: SleepQuality.TIMESKIP,
name: `Timeskip ${chalk.dim("(neutral/passage of time not recognized)")}`
},
{
value: SleepQuality.DREAMLESS,
name: `Dreamless ${chalk.dim("(good/peaceful/no dreams had or remembered)")}`
},
{
value: SleepQuality.DREAMY,
name: `Dreamy ${chalk.dim("(very good/peaceful/neutral to somewhat pleasant dreams)")}`
},
{
value: SleepQuality.ECSTASY,
name: `Ecstasy ${chalk.dim("(extremely good/very peaceful/very pleasant dreams)")}`
},
new Separator(),
{
value: undefined,
name: `Uncertain`
},
],
default: oldSleepQuality ?? SleepQuality.TIMESKIP,
pageSize: 999,
})
const oldInterruptions = options.default?.interruptions
const newInterruptionsRaw = await inquire({
...options,
type: "number",
message: "How many times did you end up waking up during the sleep?",
default: oldInterruptions ?? -1,
})
const newInterruptions = newInterruptionsRaw < 0 ? undefined : Math.round(newInterruptionsRaw)
const oldWakeTime = options.default?.wakeAt
const newWakeTime = await inquire({
...options,
type: "date",
message: "Around when did you get up?",
clearable: true,
startCleared: false,
default: oldWakeTime ?? DateTime.local().set({hour: 7, minute: 0, second: 0, millisecond: 0}).toJSDate(),
format: {
year: undefined,
weekday: "short",
month: "short",
day: "numeric",
hour: "numeric",
minute: "numeric",
second: undefined,
fractionalSecondDigits: undefined,
timeZoneName: "short",
},
startingDatePart: "hour",
deltas: {
hour: [1, 5, 10],
minute: [15, 5, 1],
default: 0
}
})
const oldWakeQuality = options.default?.wakeQuality
const newWakeQuality = await inquire({
...options,
type: "list",
message: `How'd you feel when you got up?`,
choices: [
{
value: WakeQuality.AGONIZED,
name: `Agonized ${chalk.dim("(in physical pain/barely able or unable to get out of bed)")}`
},
{
value: WakeQuality.EXHAUSTED,
name: `Exhausted ${chalk.dim("(very tired/not wanting to get out of bed)")}`
},
{
value: WakeQuality.DROWSY,
name: `Drowsy ${chalk.dim("(a bit sleepy but willing to get out of bed)")}`
},
{
value: WakeQuality.RESTED,
name: `Rested ${chalk.dim("(well rested and ready to get going)")}`
},
{
value: WakeQuality.ENERGIZED,
name: `Energized ${chalk.dim("(thoroughly recharged and eager to get up and running)")}`
},
new Separator(),
{
value: undefined,
name: `Uncertain`
},
],
default: oldWakeQuality ?? WakeQuality.DROWSY,
pageSize: 999,
})
const oldDreamJournal = options.default?.dreams
const newDreamJournalRaw = (await inquire({
...options,
type: "editor",
message: typeof oldDreamJournal === "string" ? `Edit dream journal for this sleep session:` : `Type up dream journal for this sleep session:`,
default: oldDreamJournal,
})).trimEnd()
const newDreamJournal = newDreamJournalRaw === "" ? undefined : newDreamJournalRaw
const result: SleepRecord = {
dreams: newDreamJournal,
interruptions: newInterruptions,
sleepAt: newBedTime ?? undefined,
sleepQuality: newSleepQuality,
wakeAt: newWakeTime ?? undefined,
wakeQuality: newWakeQuality,
}
return (
Object.keys(result).map((key: keyof SleepRecord) => result[key]).some(value => (value !== undefined))
? result
: null)
}

@ -1,9 +1,9 @@
import {ListQuestion} from "inquirer";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt.js";
import chalk from "chalk";
import {asDefault, identity} from "../../utils/Objects";
import {InquireFunction} from "../Inquire";
import {Suicidality} from "../../datatypes/Suicidality";
import {asDefault, identity} from "../../utils/Objects.js";
import {InquireFunction} from "../Inquire.js";
import {Suicidality} from "../../datatypes/Suicidality.js";
export interface SuicidalityPromptOptions extends Partial<Omit<ListQuestion, "name" | "type" | "choices">> {
}

@ -1,11 +1,11 @@
import {InputQuestion} from "inquirer";
import {InquireFunction} from "../Inquire";
import {personaName, personaPossessive, PersonaPrompt, personaVerb} from "../../datatypes/Persona";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt";
import {InquireFunction} from "../Inquire.js";
import {personaName, personaPossessive, PersonaPrompt, personaVerb} from "../../datatypes/Persona.js";
import {EntryMainMenuChoice, makeEntryMainMenuChoice} from "./EntryMainMenuPrompt.js";
import chalk from "chalk";
import pluralize from "pluralize";
import wordcount from "wordcount";
import {asDefault} from "../../utils/Objects";
import {asDefault} from "../../utils/Objects.js";
export interface SummaryPromptOptions extends Partial<Omit<InputQuestion, "name" | "type">>, PersonaPrompt {}

@ -0,0 +1,386 @@
/*
* Adapted from https://github.com/haversnail/inquirer-date-prompt:
* MIT License
*
* Copyright (c) 2021 Alex Havermale
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
import chalk from "chalk";
import {DateTime, DurationObjectUnits} from "luxon";
import inquirer, {Answers, DateQuestion, Question} from "inquirer";
import {Interface as ReadLineInterface, Key} from "readline";
import Prompt from "inquirer/lib/prompts/base.js";
import observe from "inquirer/lib/utils/events.js";
import {map, takeUntil} from "rxjs/operators/index.js";
import cliCursor from "cli-cursor"
import SuccessfulPromptStateData = inquirer.prompts.SuccessfulPromptStateData;
import FailedPromptStateData = inquirer.prompts.FailedPromptStateData;
declare module "inquirer" {
export interface DateQuestionOptions<AnswerT extends Answers> {
/**
* Transforms the value to display to the user.
*
* @param date
* The currently selected date in string format.
*
* @param answers
* The answers provided by the users.
*
* @param flags
* Additional information about the value.
*
* @returns
* The value to display to the user.
*/
transformer?(
date: string,
answers: AnswerT,
flags: { isDirty?: boolean; isCleared?: boolean; isFinal?: boolean },
): string | Promise<string>;
/**
* A Boolean value indicating whether the prompt is clearable.
* If `true`, pressing `backspace` or `delete` will replace the current value with `null`.
*/
clearable?: boolean;
/**
* A Boolean value indicating whether the prompt should start cleared even though a default is provided.
* If `true`, the value will start off null, but clearing it will change to the default date.
*/
startCleared?: boolean;
/**
* A specific locale to use when formatting the date.
* If no locale is provided, it will default to the user's current locale.
* @see the {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat|Intl.DateTimeFormat} docs for more info.
*/
locale?: string;
/**
* A set of options for customizing the date format.
* @see the {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat|Intl.DateTimeFormat} docs for more info.
*/
format?: Intl.DateTimeFormatOptions;
/** The date part that should be highlighted when the prompt is shown. */
startingDatePart?: Intl.DateTimeFormatPartTypes
/** The amount by which each value should change. If null, the value will not be selectable. */
deltas?: {
[key in Intl.DateTimeFormatPartTypes|"default"]?: number|[number]|[number,number]|[number,number,number]|[number,number,number,number]|0
}
}
export interface DateQuestion<AnswerT extends Answers = Answers> extends Question<AnswerT>, DateQuestionOptions<AnswerT> {
/** @inheritDoc */
type: "date"
/** If the default is not provided or null, the value will be the present date at the time that the prompt is shown. */
default?: Date|null
}
export interface QuestionMap {
["date"]: DateQuestion
}
}
/**
* A lookup object that maps each date part type to the corresponding field of a duration.
*/
const offsetLookup: Partial<Record<Intl.DateTimeFormatPartTypes, keyof DurationObjectUnits>> = {
year: "years",
month: "months",
day: "days",
hour: "hours",
minute: "minutes",
second: "seconds",
weekday: "days",
};
/**
* Returns the index of the _last_ element in the array where predicate is true, and -1 otherwise.
*/
function findLastIndex<T>(array: T[], predicate: (value: T, index: number, obj: T[]) => boolean) {
let l = array.length;
while (l--) {
if (predicate(array[l], l, array)) return l;
}
return -1;
}
/**
* Represents a date prompt.
*/
export class DateInput<AnswerT extends Answers = Answers, QuestionT extends DateQuestion<AnswerT> = DateQuestion> extends Prompt<QuestionT> {
date: DateTime
readonly transformer: DateQuestion["transformer"]
readonly clearable: boolean
readonly format: Intl.DateTimeFormatOptions
readonly deltas: NonNullable<DateQuestion["deltas"]>
done: ((state: unknown) => void)|null = null
isDirty: boolean
isCleared: boolean
cursorIndex: number
firstEditableIndex: number
lastEditableIndex: number
constructor(questions: QuestionT, rl: ReadLineInterface, answers: AnswerT) {
super(questions, rl, answers);
// Set the format object based on the user's specified options:
const { transformer, clearable, startCleared, locale, format = {}, default: date, deltas, startingDatePart } = this.opt;
this.transformer = transformer
this.deltas = deltas ?? {}
this.clearable = clearable ?? false
this.format = {
year: "numeric",
month: "numeric",
day: "numeric",
hour: "numeric",
minute: "numeric",
...format,
};
// Set the date object with either the default value or the current date:
this.date = DateTime.fromJSDate(date ?? new Date());
if (typeof locale === "string") {
this.date = this.date.setLocale(locale)
}
// Clear the default value option (so it won't be printed by the Prompt class):
this.opt.default = null;
this.isDirty = false;
this.isCleared = startCleared ?? false;
// Set the first and last indices of the editable date parts:
this.firstEditableIndex = this.dateParts.findIndex((part) => this.isDatePartEditable(part.type));
this.lastEditableIndex = findLastIndex(this.dateParts, (part) => this.isDatePartEditable(part.type));
// Set the cursor index to the first editable part:
this.cursorIndex = !!startingDatePart && this.isDatePartEditable(startingDatePart) ? this.dateParts.findIndex((part) => part.type === startingDatePart) : this.firstEditableIndex;
}
// Called by parent class:
_run(cb: DateInput["done"]) {
this.done = cb;
// Observe events:
const events = observe(this.rl);
const submit = events.line.pipe(map(() => (this.isCleared ? null : this.date.toJSDate())));
const validation = this.handleSubmitEvents(submit);
validation.success.forEach(this.onEnd.bind(this));
validation.error.forEach(this.onError.bind(this));
events.keypress.pipe(takeUntil(validation.success)).forEach(this.onKeypress.bind(this));
// Init the prompt:
cliCursor.hide();
this.render();
return this;
}
/**
* Renders the prompt.
* @param {string} [error]
*/
render(error?: string) {
let message = this.getQuestion(); // The question portion of the output, including any prefix and suffix
const { isDirty, isCleared } = this;
const isFinal = this.status === "answered";
if (!isCleared) {
const dateString = this.dateParts
.map(({ value }, index) =>
isFinal
? chalk.cyan(value)
: index === this.cursorIndex
? chalk.inverse(value)
: !isDirty
? chalk.dim(value)
: value,
)
.join("");
// Apply the transformer function if one was provided:
message += this.opt.transformer
? this.opt.transformer(dateString, this.answers as AnswerT, { isDirty, isCleared, isFinal })
: dateString;
// Display info on how to clear if the prompt is clearable:
if (this.opt.clearable && !isFinal) {
message += chalk.dim(" (<delete> to clear) ");
}
}
const bottomContent = error ? chalk.red(">> ") + error : "";
// Render the final message:
this.screen.render(message, bottomContent);
}
/**
* The end event handler.
*/
onEnd({ value }: SuccessfulPromptStateData<Date|null>) {
this.status = "answered";
// Re-render prompt
this.render();
this.screen.done();
cliCursor.show();
if (this.done !== null) {
this.done(value);
}
}
/**
* The error event handler.
*/
onError({ isValid }: FailedPromptStateData) {
this.render(isValid || undefined);
}
/**
* The array of date part objects according to the user's specified format.
*/
get dateParts() {
return this.date.toLocaleParts(this.format);
}
/**
* The currently selected date part.
*/
get currentDatePart() {
return this.dateParts[this.cursorIndex];
}
isDatePartEditable(part: Intl.DateTimeFormatPartTypes): boolean {
return offsetLookup.hasOwnProperty(part) && ((this.deltas[part] ?? this.deltas["default"] ?? 1) !== 0)
}
/**
* A Boolean value indicating whether the currently selected date part is editable.
*/
get isCurrentDatePartEditable() {
return this.isDatePartEditable(this.currentDatePart.type);
}
/**
* Moves the cursor index to the right.
*/
incrementCursorIndex() {
if (this.cursorIndex < this.lastEditableIndex) {
this.cursorIndex++;
}
}
/**
* Moves the cursor index to the left.
*/
decrementCursorIndex() {
if (this.cursorIndex > this.firstEditableIndex) {
this.cursorIndex--;
}
}
/**
* Shifts the currently selected date part to the specified offset value.
* The default value is `0`.
* @param {number} offset
*/
shiftDatePartValue(offset = 0) {
const { type } = this.currentDatePart;
const duration: DurationObjectUnits = {}
const offsetProperty = offsetLookup[type]
if (offset !== 0 && typeof offsetProperty === "string") {
duration[offsetProperty] = offset
// Set the input as "dirty" now that the initial date is being changed:
this.isDirty = true;
this.date = this.date.plus(duration)
}
}
/**
* Increments the currently selected date part by one.
*/
incrementDatePartValueBy(value = 1) {
this.shiftDatePartValue(value);
}
/**
* Decrements the currently selected date part by one.
*/
decrementDatePartValueBy(value = 1) {
this.shiftDatePartValue(-1 * value);
}
/**
* The keypress event handler.
*/
onKeypress({ key }: {key: Key}) {
// Reset cleared state if any other key is pressed:
if (this.isCleared) {
this.isCleared = false;
this.isDirty = true;
return
}
// Calculate the amount to increment/decrement by based on modifiers:
const deltas = this.deltas[this.currentDatePart.type] ?? this.deltas["default"] ?? [1, 10, 100]
const amount = ((): number => {
if (typeof deltas === "number") {
return deltas
} else {
switch (deltas.length) {
case 1:
return deltas[0]
case 2:
return (key.shift || key.meta) ? deltas[1] : deltas[0]
case 3:
return (key.shift || key.meta) ? (key.shift && key.meta) ? deltas[2] : deltas[1] : deltas[0]
case 4:
return key.shift ? key.meta ? deltas[3] : deltas[1] : key.meta ? deltas[2] : deltas[0]
}
}
})()
switch (key.name) {
case "right":
do {
this.incrementCursorIndex();
} while (!this.isCurrentDatePartEditable); // increments the cursor index until it hits an editable value
break;
case "left":
do {
this.decrementCursorIndex();
} while (!this.isCurrentDatePartEditable); // decrements the cursor index until it hits an editable value
break;
case "up":
this.incrementDatePartValueBy(amount);
break;
case "down":
this.decrementDatePartValueBy(amount);
break;
case "delete":
case "backspace":
if (this.clearable) this.isCleared = true;
break;
}
this.render();
}
}

@ -1,23 +1,26 @@
import Prompt from "inquirer/lib/prompts/base";
import observe from "inquirer/lib/utils/events";
import Prompt from "inquirer/lib/prompts/base.js";
import observe from "inquirer/lib/utils/events.js";
import {
Answers,
ChoiceOptions,
HierarchicalCheckboxChoice,
HierarchicalCheckboxQuestion,
Question,
Separator,
SeparatorOptions,
} from "inquirer";
import {Separator} from "../Inquire.js";
import {Interface as ReadLineInterface} from "readline";
import {filter} from "rxjs/operators"
import {filter} from "rxjs/operators/index.js"
import chalk from "chalk";
import {Subject} from "rxjs";
import figures from "figures";
import Paginator from "inquirer/lib/utils/paginator";
import {isPopulatedArray} from "../../utils/Arrays";
import {filter as fuzzyFilter, FilterOptions} from "fuzzy";
import ScreenManager from "inquirer/lib/utils/screen-manager";
import Paginator from "inquirer/lib/utils/paginator.js";
import {isPopulatedArray} from "../../utils/Arrays.js";
import {FilterOptions} from "fuzzy";
import fuzzy from "fuzzy";
import ScreenManager from "inquirer/lib/utils/screen-manager.js";
const fuzzyFilter = fuzzy.filter;
interface ExtendedReadLine extends ReadLineInterface {
line: string

@ -1,12 +1,12 @@
import Prompt from "inquirer/lib/prompts/base";
import observe from "inquirer/lib/utils/events";
import Prompt from "inquirer/lib/prompts/base.js";
import observe from "inquirer/lib/utils/events.js";
import {Answers, MultiTextInputQuestion, Question} from "inquirer";
import {Interface as ReadLineInterface} from "readline";
import {filter} from "rxjs/operators"
import {filter} from "rxjs/operators/index.js"
import chalk from "chalk";
import {Subject} from "rxjs";
import figures from "figures";
import Paginator from "inquirer/lib/utils/paginator";
import Paginator from "inquirer/lib/utils/paginator.js";
interface ExtendedReadLine extends ReadLineInterface {
line: string

@ -1,8 +1,10 @@
import {registerPrompt} from "inquirer";
import {MultiTextInput} from "./MultiTextInput";
import {HierarchicalCheckboxInput} from "./HierarchicalCheckboxInput";
import {registerPrompt} from "../Inquire.js";
import {MultiTextInput} from "./MultiTextInput.js";
import {HierarchicalCheckboxInput} from "./HierarchicalCheckboxInput.js";
import {DateInput} from "./DateInput.js";
export function registerPrompts() {
registerPrompt("multitext", MultiTextInput)
registerPrompt("hierarchical-checkbox", HierarchicalCheckboxInput)
registerPrompt("date", DateInput)
}

@ -1,18 +1,18 @@
import {EmpathyGuide, EmpathyGuideJTD} from "../datatypes/EmpathyGuide";
import {Entry} from "../datatypes/Entry";
import {EmpathyGuide, EmpathyGuideJTD} from "../datatypes/EmpathyGuide.js";
import {Entry} from "../datatypes/Entry.js";
import {
ReferencedSchema,
ReferencedTypes,
schema,
SchemaData,
AnyReferenceList, Value
} from "../schemata/SchemaData";
} from "../schemata/SchemaData.js";
import {rm, open, FileHandle} from "fs/promises";
import {join, dirname} from "path";
import {dump, load} from "js-yaml";
import envPaths from "env-paths";
import makeDir from "make-dir";
import {Journal, JournalJTD} from "../datatypes/Journal";
import {Journal, JournalJTD} from "../datatypes/Journal.js";
export type AutosaveFile = Record<string, unknown>
export type AutosaveFileJTD = typeof AutosaveFileJTD

@ -1,5 +1,5 @@
import AJV, {ValidateFunction, JTDSchemaType} from "ajv/dist/jtd";
import AJV, {ValidateFunction, JTDSchemaType} from "ajv/dist/jtd.js";
const ajv = new AJV();

@ -1,6 +1,6 @@
import {AnySchemaDataFor} from "./SchemaData";
import {AnySchemaDataFor} from "./SchemaData.js";
import {dump, load} from "js-yaml";
import {InquireFunction, ShowFunction} from "../prompts/Inquire";
import {InquireFunction, ShowFunction} from "../prompts/Inquire.js";
import {EditorQuestion, ExpandQuestion} from "inquirer";
const RETRY = "Retry"

@ -1,6 +1,6 @@
{
"compilerOptions": {
"module": "commonjs",
"module": "es6",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"target": "es6",

Loading…
Cancel
Save