@nightkatana/kronosys-app 1.0.0-beta.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 (179) hide show
  1. package/README.md +81 -0
  2. package/app/api/action/route.ts +16 -0
  3. package/app/api/backup/route.ts +84 -0
  4. package/app/api/health/route.ts +22 -0
  5. package/app/api/state/route.ts +27 -0
  6. package/app/apple-icon.png +0 -0
  7. package/app/changelog/page.tsx +122 -0
  8. package/app/globals.css +210 -0
  9. package/app/guide/layout.tsx +11 -0
  10. package/app/guide/page.tsx +278 -0
  11. package/app/icon.png +0 -0
  12. package/app/layout.tsx +77 -0
  13. package/app/licenses/layout.tsx +11 -0
  14. package/app/licenses/page.tsx +246 -0
  15. package/app/manifest.ts +32 -0
  16. package/app/page.tsx +1610 -0
  17. package/app/reporting/page.tsx +2943 -0
  18. package/app/settings/layout.tsx +10 -0
  19. package/app/settings/page.tsx +3518 -0
  20. package/bin/kronosys.mjs +46 -0
  21. package/components/KronosysPackageVersionProvider.tsx +19 -0
  22. package/components/KronosysPayloadProvider.tsx +109 -0
  23. package/components/PwaRegister.tsx +25 -0
  24. package/components/SiteLegalFooter.tsx +21 -0
  25. package/components/ThemeProvider.tsx +78 -0
  26. package/components/dashboard/AppShellLiveSessionDrawer.tsx +394 -0
  27. package/components/dashboard/AppShellRouteNav.tsx +131 -0
  28. package/components/dashboard/AppVersionStamp.tsx +16 -0
  29. package/components/dashboard/DashboardCollapsibleSection.tsx +57 -0
  30. package/components/dashboard/DashboardColumnHintsBanner.tsx +159 -0
  31. package/components/dashboard/DashboardCommandCenter.tsx +470 -0
  32. package/components/dashboard/DashboardLangGateModal.tsx +118 -0
  33. package/components/dashboard/DashboardLoadingOverlay.tsx +42 -0
  34. package/components/dashboard/DashboardSimpleModal.tsx +337 -0
  35. package/components/dashboard/DashboardSuspenseFallback.tsx +52 -0
  36. package/components/dashboard/DashboardToastProvider.tsx +64 -0
  37. package/components/dashboard/DashboardTour.tsx +435 -0
  38. package/components/dashboard/DeferredDescriptionPopoverWrap.tsx +39 -0
  39. package/components/dashboard/DeleteSessionModal.tsx +130 -0
  40. package/components/dashboard/DescriptionTooltipPortaled.tsx +31 -0
  41. package/components/dashboard/GitIdentityQuickSetupModal.tsx +211 -0
  42. package/components/dashboard/HeaderIntegrationBadges.tsx +69 -0
  43. package/components/dashboard/InlineMetricHelpTrigger.tsx +102 -0
  44. package/components/dashboard/IssuePickerModal.tsx +168 -0
  45. package/components/dashboard/KronoFocusPanel.tsx +834 -0
  46. package/components/dashboard/KronosysDatetimePopoverField.tsx +357 -0
  47. package/components/dashboard/KronosysTimePopoverField.tsx +233 -0
  48. package/components/dashboard/LanguageMenu.tsx +123 -0
  49. package/components/dashboard/MongoMirrorSyncLine.tsx +57 -0
  50. package/components/dashboard/NewSessionScopeModal.tsx +410 -0
  51. package/components/dashboard/PageRefreshButton.tsx +130 -0
  52. package/components/dashboard/PlainHelpPopover.tsx +97 -0
  53. package/components/dashboard/ReportingPageToc.tsx +68 -0
  54. package/components/dashboard/ReportingTour.tsx +342 -0
  55. package/components/dashboard/SavedProjectPicker.tsx +92 -0
  56. package/components/dashboard/SavedTagPicker.tsx +115 -0
  57. package/components/dashboard/ScrollToTopFab.tsx +41 -0
  58. package/components/dashboard/SelectedSessionSidebarBlock.tsx +630 -0
  59. package/components/dashboard/SessionEndReasonEditor.tsx +114 -0
  60. package/components/dashboard/SessionListPanel.tsx +320 -0
  61. package/components/dashboard/SessionLocMetricsSection.tsx +128 -0
  62. package/components/dashboard/SettingsTagsProjectsSection.tsx +993 -0
  63. package/components/dashboard/SettingsTour.tsx +332 -0
  64. package/components/dashboard/TagPills.tsx +149 -0
  65. package/components/dashboard/TagsHelpTrigger.tsx +84 -0
  66. package/components/dashboard/TaskFocusPanel.tsx +1261 -0
  67. package/components/dashboard/TaskSessionLiveCard.tsx +832 -0
  68. package/components/dashboard/TaskSubtasksBlock.tsx +748 -0
  69. package/components/dashboard/ThemeToggle.test.tsx +26 -0
  70. package/components/dashboard/ThemeToggle.tsx +36 -0
  71. package/components/dashboard/UserGuideBodyText.tsx +62 -0
  72. package/components/dashboard/WorkspaceGitRepoCard.tsx +191 -0
  73. package/components/dashboard/taskFieldStyles.ts +139 -0
  74. package/components/dashboard/useAnchoredFloatingPortalStyle.ts +71 -0
  75. package/components/dashboard/useDescriptionPopoverAfterMs.ts +220 -0
  76. package/components/dashboard/useKronoFocusLiveSeconds.ts +36 -0
  77. package/components/dashboard/useSmoothStopwatchMs.ts +25 -0
  78. package/lib/appShellHeaderClasses.ts +12 -0
  79. package/lib/backupCsvExport.test.ts +149 -0
  80. package/lib/backupCsvExport.ts +392 -0
  81. package/lib/changelogCopy.ts +34 -0
  82. package/lib/concurrentTaskStartPreference.ts +29 -0
  83. package/lib/dashboardClockFormat.ts +13 -0
  84. package/lib/dashboardColumnChrome.ts +3 -0
  85. package/lib/dashboardColumnHintsStorage.ts +57 -0
  86. package/lib/dashboardCopy.ts +1831 -0
  87. package/lib/dashboardDetachedUrlHintStorage.ts +24 -0
  88. package/lib/dashboardGitIdentityBannerStorage.ts +36 -0
  89. package/lib/dashboardLangStorage.ts +72 -0
  90. package/lib/dashboardQuickSearch.ts +476 -0
  91. package/lib/dashboardQuickSearchQuery.test.ts +63 -0
  92. package/lib/dashboardQuickSearchQuery.ts +179 -0
  93. package/lib/dashboardSessionNav.ts +33 -0
  94. package/lib/dashboardShortcuts.ts +268 -0
  95. package/lib/dashboardTimeZone.ts +91 -0
  96. package/lib/dashboardTourStorage.ts +68 -0
  97. package/lib/dataDir.test.ts +87 -0
  98. package/lib/dataDir.ts +83 -0
  99. package/lib/devDataPreferenceFile.ts +55 -0
  100. package/lib/devDataRuntimeInfo.ts +34 -0
  101. package/lib/formatIsoShort.test.ts +46 -0
  102. package/lib/formatIsoShort.ts +29 -0
  103. package/lib/generatedUserChangelog.ts +34 -0
  104. package/lib/gitlabIssueSearch.ts +8 -0
  105. package/lib/kronoFocusDurationHistory.ts +71 -0
  106. package/lib/kronoFocusRhythm.test.ts +130 -0
  107. package/lib/kronoFocusRhythm.ts +46 -0
  108. package/lib/kronoFocusTimerUrgency.test.ts +74 -0
  109. package/lib/kronoFocusTimerUrgency.ts +24 -0
  110. package/lib/kronosysApi.ts +143 -0
  111. package/lib/legacyEditorPayloadKeys.ts +52 -0
  112. package/lib/legacyKronoFocusStorageKeys.test.ts +29 -0
  113. package/lib/legacyKronoFocusStorageKeys.ts +32 -0
  114. package/lib/licensesCopy.ts +128 -0
  115. package/lib/openPlainTextInNewTab.ts +49 -0
  116. package/lib/readKronosysPackageVersion.ts +10 -0
  117. package/lib/reportingAggregate.test.ts +325 -0
  118. package/lib/reportingAggregate.ts +819 -0
  119. package/lib/reportingDatePresets.ts +41 -0
  120. package/lib/reportingMetricHelp.ts +430 -0
  121. package/lib/reportingNonFinalIndicators.test.ts +157 -0
  122. package/lib/reportingNonFinalIndicators.ts +102 -0
  123. package/lib/reportingStrings.ts +491 -0
  124. package/lib/reportingTagWeekBreakdown.test.ts +141 -0
  125. package/lib/reportingTagWeekBreakdown.ts +181 -0
  126. package/lib/reportingWeekLayout.test.ts +239 -0
  127. package/lib/reportingWeekLayout.ts +313 -0
  128. package/lib/sessionAssiduity.test.ts +25 -0
  129. package/lib/sessionAssiduity.ts +33 -0
  130. package/lib/sessionEndReason.ts +55 -0
  131. package/lib/sessionEndWarnings.test.ts +200 -0
  132. package/lib/sessionEndWarnings.ts +125 -0
  133. package/lib/sessionListMerge.test.ts +101 -0
  134. package/lib/sessionListMerge.ts +70 -0
  135. package/lib/sessionTaskSidebarStats.test.ts +24 -0
  136. package/lib/sessionTaskSidebarStats.ts +54 -0
  137. package/lib/settingsCopy.ts +1276 -0
  138. package/lib/taskParsing.test.ts +153 -0
  139. package/lib/taskParsing.ts +737 -0
  140. package/lib/theme.ts +15 -0
  141. package/lib/translucentButtonClasses.ts +34 -0
  142. package/lib/usageProfile.test.ts +84 -0
  143. package/lib/usageProfile.ts +52 -0
  144. package/lib/userGuideCopy.ts +464 -0
  145. package/lib/workspaceLocDefaults.ts +21 -0
  146. package/next-env.d.ts +6 -0
  147. package/next.config.ts +15 -0
  148. package/package.json +87 -0
  149. package/postcss.config.mjs +12 -0
  150. package/public/apple-icon.png +0 -0
  151. package/public/favicon.ico +0 -0
  152. package/public/file.svg +1 -0
  153. package/public/globe.svg +1 -0
  154. package/public/icon-192.png +0 -0
  155. package/public/icon-512.png +0 -0
  156. package/public/icon.png +0 -0
  157. package/public/next.svg +1 -0
  158. package/public/sw.js +13 -0
  159. package/public/traceback.png +0 -0
  160. package/public/vercel.svg +1 -0
  161. package/public/window.svg +1 -0
  162. package/server/actionDispatch.test.ts +723 -0
  163. package/server/actionDispatch.ts +1476 -0
  164. package/server/actionTaskSession.test.ts +713 -0
  165. package/server/actionTaskSession.ts +717 -0
  166. package/server/db.ts +42 -0
  167. package/server/defaultCfg.ts +87 -0
  168. package/server/gitlabTokenStore.ts +34 -0
  169. package/server/kronoFocusHydrate.test.ts +142 -0
  170. package/server/kronoFocusHydrate.ts +69 -0
  171. package/server/kronoFocusMigrate.test.ts +53 -0
  172. package/server/kronoFocusMigrate.ts +78 -0
  173. package/server/mainTimerHydrate.test.ts +65 -0
  174. package/server/mainTimerHydrate.ts +53 -0
  175. package/server/payloadStore.test.ts +78 -0
  176. package/server/payloadStore.ts +83 -0
  177. package/server/sessionWallHydrate.test.ts +46 -0
  178. package/server/sessionWallHydrate.ts +88 -0
  179. package/tsconfig.json +41 -0
@@ -0,0 +1,92 @@
1
+ "use client";
2
+
3
+ import { Fragment } from "react";
4
+ import { formatProjectDisplay, normalizeProjectKey } from "@/lib/taskParsing";
5
+ import {
6
+ PROJECT_INLINE_SUGGEST,
7
+ PROJECT_INLINE_SUGGEST_SELECTED,
8
+ } from "./taskFieldStyles";
9
+ import { useDescriptionPopoverAfterMs } from "./useDescriptionPopoverAfterMs";
10
+
11
+ function SuggestedProjectButton({
12
+ project,
13
+ selected,
14
+ onPick,
15
+ description,
16
+ }: {
17
+ project: string;
18
+ selected: boolean;
19
+ onPick: () => void;
20
+ description: string | undefined;
21
+ }) {
22
+ const { hasDescription, triggerProps, popoverLayer, anchorWrapperProps } =
23
+ useDescriptionPopoverAfterMs(description);
24
+
25
+ const chipClass = selected
26
+ ? `${PROJECT_INLINE_SUGGEST_SELECTED} cursor-pointer transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/35 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900`
27
+ : `${PROJECT_INLINE_SUGGEST} cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-sky-500/30 focus-visible:ring-offset-2 focus-visible:ring-offset-white dark:focus-visible:ring-offset-zinc-900`;
28
+
29
+ const btn = (
30
+ <button
31
+ type="button"
32
+ className={chipClass}
33
+ aria-pressed={selected ? "true" : "false"}
34
+ {...triggerProps}
35
+ onClick={onPick}
36
+ >
37
+ {formatProjectDisplay(project)}
38
+ </button>
39
+ );
40
+
41
+ if (!hasDescription) {
42
+ return btn;
43
+ }
44
+
45
+ return (
46
+ <Fragment>
47
+ <span className="relative inline-flex max-w-full shrink-0" {...anchorWrapperProps}>
48
+ {btn}
49
+ </span>
50
+ {popoverLayer}
51
+ </Fragment>
52
+ );
53
+ }
54
+
55
+ export function SavedProjectPicker({
56
+ knownProjects,
57
+ selectedProject,
58
+ onPickProject,
59
+ projectDescriptions,
60
+ noTopMargin,
61
+ }: {
62
+ knownProjects: string[];
63
+ selectedProject?: string | null;
64
+ /** `null` : désactiver le projet (clic sur la pastille déjà active). */
65
+ onPickProject: (name: string | null) => void;
66
+ projectDescriptions?: Record<string, string>;
67
+ noTopMargin?: boolean;
68
+ }) {
69
+ const sel = selectedProject?.trim() ? normalizeProjectKey(selectedProject) : "";
70
+ const selLower = sel.toLowerCase();
71
+ if (knownProjects.length === 0) {
72
+ return null;
73
+ }
74
+ return (
75
+ <div className={`min-w-0 shrink-0 ${noTopMargin ? "" : "mt-1.5"}`}>
76
+ <div className="flex flex-nowrap items-baseline gap-x-2 gap-y-1 overflow-x-auto pb-0.5 [scrollbar-width:thin]">
77
+ {knownProjects.map((p) => {
78
+ const on = normalizeProjectKey(p).toLowerCase() === selLower;
79
+ return (
80
+ <SuggestedProjectButton
81
+ key={p}
82
+ project={p}
83
+ selected={on}
84
+ description={projectDescriptions?.[normalizeProjectKey(p).toLowerCase()]}
85
+ onPick={() => onPickProject(on ? null : p)}
86
+ />
87
+ );
88
+ })}
89
+ </div>
90
+ </div>
91
+ );
92
+ }
@@ -0,0 +1,115 @@
1
+ "use client";
2
+
3
+ import { Fragment } from "react";
4
+ import {
5
+ filterKnownTagsForProject,
6
+ formatTagDisplay,
7
+ isProjectScopedTag,
8
+ normalizeTagKey,
9
+ } from "@/lib/taskParsing";
10
+ import {
11
+ TAG_INLINE_SUGGEST,
12
+ TAG_INLINE_SUGGEST_SCOPED_SELECTED,
13
+ TAG_INLINE_SUGGEST_SELECTED,
14
+ } from "./taskFieldStyles";
15
+ import { useDescriptionPopoverAfterMs } from "./useDescriptionPopoverAfterMs";
16
+
17
+ function SuggestedTagButton({
18
+ tag,
19
+ selected,
20
+ scopedSelectedOutline,
21
+ onPick,
22
+ description,
23
+ }: {
24
+ tag: string;
25
+ selected: boolean;
26
+ scopedSelectedOutline: boolean;
27
+ onPick: () => void;
28
+ description: string | undefined;
29
+ }) {
30
+ const { hasDescription, triggerProps, popoverLayer, anchorWrapperProps } =
31
+ useDescriptionPopoverAfterMs(description);
32
+
33
+ const chipClass = selected
34
+ ? scopedSelectedOutline
35
+ ? TAG_INLINE_SUGGEST_SCOPED_SELECTED
36
+ : TAG_INLINE_SUGGEST_SELECTED
37
+ : TAG_INLINE_SUGGEST;
38
+
39
+ const btn = (
40
+ <button
41
+ type="button"
42
+ className={chipClass}
43
+ aria-pressed={selected ? "true" : "false"}
44
+ {...triggerProps}
45
+ onClick={onPick}
46
+ >
47
+ {formatTagDisplay(tag)}
48
+ </button>
49
+ );
50
+
51
+ if (!hasDescription) {
52
+ return btn;
53
+ }
54
+
55
+ return (
56
+ <Fragment>
57
+ <span className="relative inline-flex max-w-full shrink-0" {...anchorWrapperProps}>
58
+ {btn}
59
+ </span>
60
+ {popoverLayer}
61
+ </Fragment>
62
+ );
63
+ }
64
+
65
+ export function SavedTagPicker({
66
+ knownTags,
67
+ selectedTags,
68
+ onPickTag,
69
+ /** Projet courant : les étiquettes `@projet#code` (stockées `projet#code`) d’un autre projet sont masquées. */
70
+ project,
71
+ tagDescriptions,
72
+ /** Désactive la marge du haut quand le parent gère déjà l’espacement (ex. bloc étiquettes). */
73
+ noTopMargin,
74
+ }: {
75
+ knownTags: string[];
76
+ selectedTags: string[];
77
+ /** Clic sur une pastille : activer ou désactiver l’étiquette sur la tâche / le brouillon. */
78
+ onPickTag: (tag: string) => void;
79
+ project?: string | null;
80
+ tagDescriptions?: Record<string, string>;
81
+ noTopMargin?: boolean;
82
+ }) {
83
+ const selectedKeys = new Set(selectedTags.map((t) => normalizeTagKey(t).toLowerCase()));
84
+ const scopedPool = filterKnownTagsForProject(knownTags, project);
85
+ const describe = (tag: string) => {
86
+ if (!tagDescriptions || Object.keys(tagDescriptions).length === 0) {
87
+ return undefined;
88
+ }
89
+ const k = normalizeTagKey(tag).toLowerCase();
90
+ return tagDescriptions[k] ?? tagDescriptions[normalizeTagKey(tag)];
91
+ };
92
+ if (knownTags.length === 0 || scopedPool.length === 0) {
93
+ return null;
94
+ }
95
+ return (
96
+ <div className={`min-w-0 shrink-0 ${noTopMargin ? "" : "mt-1.5"}`}>
97
+ <div className="flex flex-nowrap items-baseline gap-x-2 gap-y-1 overflow-x-auto pb-0.5 [scrollbar-width:thin]">
98
+ {scopedPool.map((tag) => {
99
+ const nk = normalizeTagKey(tag).toLowerCase();
100
+ const selected = selectedKeys.has(nk);
101
+ return (
102
+ <SuggestedTagButton
103
+ key={tag}
104
+ tag={tag}
105
+ selected={selected}
106
+ scopedSelectedOutline={selected && isProjectScopedTag(normalizeTagKey(tag))}
107
+ description={describe(tag)}
108
+ onPick={() => onPickTag(tag)}
109
+ />
110
+ );
111
+ })}
112
+ </div>
113
+ </div>
114
+ );
115
+ }
@@ -0,0 +1,41 @@
1
+ "use client";
2
+
3
+ import { ChevronUp } from "lucide-react";
4
+ import { useCallback, useEffect, useState } from "react";
5
+
6
+ const SHOW_AFTER_PX = 320;
7
+
8
+ export function ScrollToTopFab({ ariaLabel }: Readonly<{ ariaLabel: string }>) {
9
+ const [visible, setVisible] = useState(false);
10
+
11
+ useEffect(() => {
12
+ const onScroll = () => {
13
+ setVisible(globalThis.scrollY > SHOW_AFTER_PX);
14
+ };
15
+ onScroll();
16
+ globalThis.addEventListener("scroll", onScroll, { passive: true });
17
+ return () => globalThis.removeEventListener("scroll", onScroll);
18
+ }, []);
19
+
20
+ const goTop = useCallback(() => {
21
+ const reduce =
22
+ typeof globalThis.matchMedia === "function" &&
23
+ globalThis.matchMedia("(prefers-reduced-motion: reduce)").matches;
24
+ globalThis.scrollTo({ top: 0, behavior: reduce ? "auto" : "smooth" });
25
+ }, []);
26
+
27
+ if (!visible) {
28
+ return null;
29
+ }
30
+
31
+ return (
32
+ <button
33
+ type="button"
34
+ onClick={goTop}
35
+ aria-label={ariaLabel}
36
+ className="fixed bottom-6 right-4 z-40 flex h-11 w-11 items-center justify-center rounded-full border border-zinc-300 bg-white/95 text-zinc-700 shadow-lg shadow-zinc-900/10 backdrop-blur-sm transition-colors hover:border-violet-500/60 hover:bg-violet-50 hover:text-violet-800 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-violet-500 dark:border-zinc-600 dark:bg-zinc-900/95 dark:text-zinc-200 dark:shadow-black/40 dark:hover:border-violet-500/70 dark:hover:bg-zinc-800 dark:hover:text-violet-100 sm:right-6"
37
+ >
38
+ <ChevronUp className="h-6 w-6" strokeWidth={2.25} aria-hidden />
39
+ </button>
40
+ );
41
+ }