@nightkatana/kronosys-app 1.0.0-beta.2 → 1.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +28 -1
- package/app/api/action/route.ts +39 -3
- package/app/api/action-logs/route.ts +24 -0
- package/app/api/backup/route.ts +1 -1
- package/app/api/restore/route.ts +145 -0
- package/app/changelog/page.tsx +71 -4
- package/app/globals.css +127 -0
- package/app/guide/page.tsx +61 -15
- package/app/implementation/page.tsx +700 -0
- package/app/layout.tsx +14 -3
- package/app/licenses/page.tsx +99 -37
- package/app/logs/page.tsx +258 -0
- package/app/manifest.ts +5 -5
- package/app/page.tsx +784 -229
- package/app/reporting/page.tsx +1266 -474
- package/app/settings/page.tsx +252 -18
- package/bin/kronosys.mjs +140 -15
- package/components/KronosysPayloadProvider.tsx +2 -0
- package/components/RouteTransition.tsx +18 -0
- package/components/dashboard/AppShellCommandCenterPlaceholder.tsx +17 -0
- package/components/dashboard/AppShellHeaderSessionMeta.tsx +210 -0
- package/components/dashboard/AppShellHeaderWallClock.tsx +54 -0
- package/components/dashboard/AppShellLiveSessionDrawer.tsx +154 -38
- package/components/dashboard/AppShellRouteNav.tsx +323 -48
- package/components/dashboard/DashboardPauseBackdrop.tsx +50 -0
- package/components/dashboard/DashboardSimpleModal.tsx +168 -25
- package/components/dashboard/DashboardTour.tsx +115 -29
- package/components/dashboard/GlobalPauseConfirmModal.tsx +183 -0
- package/components/dashboard/KronosysDatetimePopoverField.tsx +167 -122
- package/components/dashboard/KronosysTimePopoverField.tsx +54 -12
- package/components/dashboard/NewSessionScopeModal.tsx +211 -20
- package/components/dashboard/PlannedTaskBoundaryConflictWatcher.tsx +275 -0
- package/components/dashboard/ReportingTour.tsx +87 -21
- package/components/dashboard/SavedProjectPicker.tsx +16 -3
- package/components/dashboard/SelectedSessionSidebarBlock.tsx +512 -142
- package/components/dashboard/SessionListPanel.tsx +327 -44
- package/components/dashboard/SettingsTagsProjectsSection.tsx +1073 -264
- package/components/dashboard/SettingsTaskTemplatesSection.tsx +316 -0
- package/components/dashboard/SettingsTour.tsx +86 -21
- package/components/dashboard/TagPills.tsx +14 -1
- package/components/dashboard/TaskFocusPanel.tsx +1081 -478
- package/components/dashboard/TaskSessionLiveCard.tsx +650 -135
- package/components/dashboard/TaskTimelineGanttModal.tsx +601 -0
- package/components/dashboard/taskFieldStyles.ts +20 -4
- package/components/dashboard/useReportingInteractionState.ts +80 -0
- package/lib/appShellHeaderClasses.ts +13 -0
- package/lib/businessRulesMatrix.ts +210 -0
- package/lib/copyToClipboard.ts +43 -0
- package/lib/dashboardCopy.ts +494 -84
- package/lib/dashboardQuickSearch.ts +54 -2
- package/lib/dashboardTimeZone.ts +109 -0
- package/lib/formatAppShellWallClock.ts +66 -0
- package/lib/formatSessionNameTemplate.ts +141 -0
- package/lib/generatedUserChangelog.ts +177 -6
- package/lib/globalPausePreview.ts +292 -0
- package/lib/implementationNotes.ts +1188 -0
- package/lib/kronosysApi.ts +6 -0
- package/lib/kronosysDashboardModalGates.ts +24 -0
- package/lib/plannedBoundaryAttention.ts +9 -0
- package/lib/plannedBoundaryConflict.ts +23 -0
- package/lib/reportingAggregate.ts +517 -75
- package/lib/reportingMetricHelp.ts +8 -0
- package/lib/reportingStrings.ts +37 -3
- package/lib/sessionListMerge.ts +4 -0
- package/lib/sessionTaskSidebarStats.ts +182 -21
- package/lib/settingsCopy.ts +178 -4
- package/lib/taskParsing.ts +360 -103
- package/lib/taskTemplateDraft.ts +135 -0
- package/lib/taskTimelineGantt.ts +265 -0
- package/lib/temporalDisplayPlanned.ts +71 -0
- package/lib/userGuideCopy.ts +121 -47
- package/next.config.ts +7 -0
- package/package.json +12 -24
- package/server/actionDispatch.ts +1000 -77
- package/server/actionTaskSession.ts +337 -24
- package/server/db.ts +7 -15
- package/server/dbSchema.ts +24 -0
- package/server/defaultCfg.ts +5 -0
- package/server/gitlabTokenStore.ts +0 -12
- package/server/liveHistorySync.ts +53 -0
- package/server/mainTimerHydrate.ts +38 -2
- package/server/payloadStore.ts +33 -11
- package/server/sessionWallHydrate.ts +66 -3
- package/server/userActionLog.ts +126 -0
- package/sonar-project.properties +11 -0
- package/tsconfig.json +2 -1
- package/components/dashboard/IssuePickerModal.tsx +0 -168
- package/components/dashboard/ThemeToggle.test.tsx +0 -26
- package/lib/backupCsvExport.test.ts +0 -149
- package/lib/dashboardQuickSearchQuery.test.ts +0 -63
- package/lib/dataDir.test.ts +0 -87
- package/lib/formatIsoShort.test.ts +0 -46
- package/lib/kronoFocusRhythm.test.ts +0 -130
- package/lib/kronoFocusTimerUrgency.test.ts +0 -74
- package/lib/legacyKronoFocusStorageKeys.test.ts +0 -29
- package/lib/reportingAggregate.test.ts +0 -325
- package/lib/reportingNonFinalIndicators.test.ts +0 -157
- package/lib/reportingTagWeekBreakdown.test.ts +0 -141
- package/lib/reportingWeekLayout.test.ts +0 -239
- package/lib/sessionAssiduity.test.ts +0 -25
- package/lib/sessionEndWarnings.test.ts +0 -200
- package/lib/sessionListMerge.test.ts +0 -101
- package/lib/sessionTaskSidebarStats.test.ts +0 -24
- package/lib/taskParsing.test.ts +0 -153
- package/lib/usageProfile.test.ts +0 -84
- package/server/actionDispatch.test.ts +0 -723
- package/server/actionTaskSession.test.ts +0 -713
- package/server/kronoFocusHydrate.test.ts +0 -142
- package/server/kronoFocusMigrate.test.ts +0 -53
- package/server/mainTimerHydrate.test.ts +0 -65
- package/server/payloadStore.test.ts +0 -78
- package/server/sessionWallHydrate.test.ts +0 -46
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import type { DashboardStrings } from "@/lib/dashboardCopy";
|
|
2
|
+
import type { ParsedTaskTemplateRow } from "@/lib/taskTemplateDraft";
|
|
3
|
+
import { formatTaskTemplateDraftLine } from "@/lib/taskTemplateDraft";
|
|
2
4
|
import type { SessionListEntry } from "@/components/dashboard/SessionListPanel";
|
|
3
5
|
import {
|
|
4
6
|
formatDuration,
|
|
@@ -313,13 +315,28 @@ export function buildDashboardQuickSearchItems(opts: {
|
|
|
313
315
|
dt: DashboardStrings;
|
|
314
316
|
/** Session live (pour l’état actif / pause dans les résultats). */
|
|
315
317
|
liveSearchContext?: SearchLiveContext | null;
|
|
318
|
+
/** Modèles de tâche (payload `taskTemplates`). */
|
|
319
|
+
taskTemplates?: ParsedTaskTemplateRow[];
|
|
320
|
+
/** Applique le texte du modèle au champ « nouvelle tâche » (recherche rapide). */
|
|
321
|
+
onSelectTaskTemplate?: (draftLine: string) => void;
|
|
316
322
|
onSelectSession: (sessionId: string) => void | Promise<void>;
|
|
317
323
|
scrollToSession: (sessionId: string) => void;
|
|
318
324
|
focusTasksColumn: () => void;
|
|
319
325
|
scrollToTask: (taskId: string) => void;
|
|
320
326
|
}): DashboardSearchItem[] {
|
|
321
|
-
const {
|
|
322
|
-
|
|
327
|
+
const {
|
|
328
|
+
history,
|
|
329
|
+
historyArchived,
|
|
330
|
+
sessionCurrent,
|
|
331
|
+
dt,
|
|
332
|
+
liveSearchContext,
|
|
333
|
+
taskTemplates,
|
|
334
|
+
onSelectTaskTemplate,
|
|
335
|
+
onSelectSession,
|
|
336
|
+
scrollToSession,
|
|
337
|
+
focusTasksColumn,
|
|
338
|
+
scrollToTask,
|
|
339
|
+
} = opts;
|
|
323
340
|
const items: DashboardSearchItem[] = [];
|
|
324
341
|
const seenSid = new Set<string>();
|
|
325
342
|
|
|
@@ -472,5 +489,40 @@ export function buildDashboardQuickSearchItems(opts: {
|
|
|
472
489
|
});
|
|
473
490
|
}
|
|
474
491
|
|
|
492
|
+
if (taskTemplates && taskTemplates.length > 0 && onSelectTaskTemplate) {
|
|
493
|
+
const tmplHayExtra =
|
|
494
|
+
"template modèle modèle de tâche task template gabarit saved task template";
|
|
495
|
+
for (const tpl of taskTemplates) {
|
|
496
|
+
const tid = typeof tpl.id === "string" ? tpl.id.trim() : "";
|
|
497
|
+
if (!tid) {
|
|
498
|
+
continue;
|
|
499
|
+
}
|
|
500
|
+
const draft = formatTaskTemplateDraftLine(tpl);
|
|
501
|
+
if (!draft.trim()) {
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
const rawName = typeof tpl.name === "string" ? tpl.name : "";
|
|
505
|
+
const idx = buildTaskSearchIndex(rawName, tpl.tags, tpl.project);
|
|
506
|
+
const displayTitle =
|
|
507
|
+
taskTitleForDisplay(rawName).trim() || tid.slice(0, 8);
|
|
508
|
+
const hay = `${rawName} ${displayTitle} ${tid} ${draft} ${idx.haystackFragment} ${tmplHayExtra} ${dt.dataSearchKindTaskTemplate}`
|
|
509
|
+
.trim()
|
|
510
|
+
.toLowerCase();
|
|
511
|
+
items.push({
|
|
512
|
+
id: `search-task-template-${tid}`,
|
|
513
|
+
title: displayTitle,
|
|
514
|
+
subtitle: dt.dataSearchKindTaskTemplate,
|
|
515
|
+
metaLine: idx.metaLine.trim()
|
|
516
|
+
? truncateMetaLine(idx.metaLine, 160)
|
|
517
|
+
: undefined,
|
|
518
|
+
haystack: hay,
|
|
519
|
+
onSelect: () => {
|
|
520
|
+
focusTasksColumn();
|
|
521
|
+
onSelectTaskTemplate(draft);
|
|
522
|
+
},
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
|
|
475
527
|
return dedupeSearchItemsById(items);
|
|
476
528
|
}
|
package/lib/dashboardTimeZone.ts
CHANGED
|
@@ -89,3 +89,112 @@ export function calendarDateKeyInTimeZone(iso: string | null | undefined, timeZo
|
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Décalage `local - utc` (millisecondes) du fuseau au point dans le temps `utcMs`.
|
|
95
|
+
* Tient compte des transitions DST (le décalage varie en cours d’année).
|
|
96
|
+
*/
|
|
97
|
+
function timeZoneOffsetMsAtUtc(utcMs: number, timeZone: string): number {
|
|
98
|
+
const fmt = new Intl.DateTimeFormat("en-CA", {
|
|
99
|
+
timeZone,
|
|
100
|
+
year: "numeric",
|
|
101
|
+
month: "2-digit",
|
|
102
|
+
day: "2-digit",
|
|
103
|
+
hour: "2-digit",
|
|
104
|
+
minute: "2-digit",
|
|
105
|
+
second: "2-digit",
|
|
106
|
+
hourCycle: "h23",
|
|
107
|
+
});
|
|
108
|
+
const parts = fmt.formatToParts(new Date(utcMs));
|
|
109
|
+
const get = (t: string): number => {
|
|
110
|
+
const v = parts.find((p) => p.type === t)?.value ?? "0";
|
|
111
|
+
return Number.parseInt(v, 10) || 0;
|
|
112
|
+
};
|
|
113
|
+
const y = get("year");
|
|
114
|
+
const m = get("month");
|
|
115
|
+
const d = get("day");
|
|
116
|
+
const h = get("hour") % 24;
|
|
117
|
+
const min = get("minute");
|
|
118
|
+
const s = get("second");
|
|
119
|
+
const localAsUtc = Date.UTC(y, m - 1, d, h, min, s);
|
|
120
|
+
return localAsUtc - utcMs;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Instant UTC (ms) correspondant à minuit local (`00:00:00`) du jour calendaire indiqué dans
|
|
125
|
+
* le fuseau donné. Itère jusqu’à convergence (en pratique 1 ou 2 itérations) pour gérer
|
|
126
|
+
* correctement les transitions DST : `utc = localMs - offset(utc)`.
|
|
127
|
+
*
|
|
128
|
+
* Les valeurs `month`/`day` peuvent dépasser leurs bornes (ex. `day = 32`) ; `Date.UTC` reporte
|
|
129
|
+
* naturellement sur le mois suivant — pratique pour calculer la borne de fin d’un jour.
|
|
130
|
+
*/
|
|
131
|
+
export function calendarDayStartUtcMsInTimeZone(
|
|
132
|
+
year: number,
|
|
133
|
+
month: number,
|
|
134
|
+
day: number,
|
|
135
|
+
timeZone: string,
|
|
136
|
+
): number {
|
|
137
|
+
const tz = timeZone.trim() || DEFAULT_DASHBOARD_TIME_ZONE;
|
|
138
|
+
if (!isValidIanaTimeZone(tz)) {
|
|
139
|
+
return Date.UTC(year, month - 1, day, 0, 0, 0, 0);
|
|
140
|
+
}
|
|
141
|
+
/** Cible exprimée en « millisecondes locales » (vue UTC d’une horloge locale à minuit). */
|
|
142
|
+
const localMs = Date.UTC(year, month - 1, day, 0, 0, 0, 0);
|
|
143
|
+
let guess = localMs;
|
|
144
|
+
for (let pass = 0; pass < 4; pass += 1) {
|
|
145
|
+
const off = timeZoneOffsetMsAtUtc(guess, tz);
|
|
146
|
+
const next = localMs - off;
|
|
147
|
+
if (next === guess) {
|
|
148
|
+
return guess;
|
|
149
|
+
}
|
|
150
|
+
guess = next;
|
|
151
|
+
}
|
|
152
|
+
return guess;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Découpe l’intervalle `[startMs, endMs[` (UTC) par jour calendaire local et renvoie la durée
|
|
157
|
+
* (minutes) tombant dans chaque jour. Renvoie une map vide si l’intervalle est vide ou invalide.
|
|
158
|
+
*
|
|
159
|
+
* Le bord supérieur est exclusif : un événement qui finit pile à minuit est compté entièrement
|
|
160
|
+
* sur le jour précédent (cas limite peu fréquent en pratique).
|
|
161
|
+
*/
|
|
162
|
+
export function splitMinutesByCalendarDay(
|
|
163
|
+
startMs: number,
|
|
164
|
+
endMs: number,
|
|
165
|
+
timeZone: string,
|
|
166
|
+
): Map<string, number> {
|
|
167
|
+
const out = new Map<string, number>();
|
|
168
|
+
if (!Number.isFinite(startMs) || !Number.isFinite(endMs) || endMs <= startMs) {
|
|
169
|
+
return out;
|
|
170
|
+
}
|
|
171
|
+
const tz = timeZone.trim() || DEFAULT_DASHBOARD_TIME_ZONE;
|
|
172
|
+
/** Garde-fou : on borne le nombre d’itérations pour ne pas boucler sur un fuseau invalide. */
|
|
173
|
+
const maxDays = 366 * 5;
|
|
174
|
+
let cursor = startMs;
|
|
175
|
+
for (let i = 0; i < maxDays && cursor < endMs; i += 1) {
|
|
176
|
+
const dayKey = calendarDateKeyInTimeZone(new Date(cursor).toISOString(), tz);
|
|
177
|
+
if (!dayKey) {
|
|
178
|
+
return new Map();
|
|
179
|
+
}
|
|
180
|
+
const [yStr, mStr, dStr] = dayKey.split("-");
|
|
181
|
+
const y = Number.parseInt(yStr, 10);
|
|
182
|
+
const m = Number.parseInt(mStr, 10);
|
|
183
|
+
const d = Number.parseInt(dStr, 10);
|
|
184
|
+
if (!Number.isFinite(y) || !Number.isFinite(m) || !Number.isFinite(d)) {
|
|
185
|
+
return new Map();
|
|
186
|
+
}
|
|
187
|
+
const nextDayStart = calendarDayStartUtcMsInTimeZone(y, m, d + 1, tz);
|
|
188
|
+
const sliceEnd = Math.min(endMs, nextDayStart);
|
|
189
|
+
if (sliceEnd <= cursor) {
|
|
190
|
+
/** Sécurité : si on n’a pas progressé (fuseau pathologique), on s’arrête. */
|
|
191
|
+
break;
|
|
192
|
+
}
|
|
193
|
+
const minutes = (sliceEnd - cursor) / 60000;
|
|
194
|
+
if (minutes > 0) {
|
|
195
|
+
out.set(dayKey, (out.get(dayKey) ?? 0) + minutes);
|
|
196
|
+
}
|
|
197
|
+
cursor = sliceEnd;
|
|
198
|
+
}
|
|
199
|
+
return out;
|
|
200
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import type { Lang } from "./dashboardCopy";
|
|
2
|
+
import { readDashboardUse24HourClockFromCfg } from "./dashboardClockFormat";
|
|
3
|
+
import { readDashboardTimeZoneFromCfg } from "./dashboardTimeZone";
|
|
4
|
+
|
|
5
|
+
export type AppShellWallClockParts = {
|
|
6
|
+
timeZone: string;
|
|
7
|
+
display: string;
|
|
8
|
+
ariaDatetime: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
function intlLocale(lang: Lang): string {
|
|
12
|
+
return lang === "fr" ? "fr-CA" : "en-CA";
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Heure murale affichée dans l’en-tête : fuseau et format 12/24 h alignés sur les paramètres tableau de bord.
|
|
17
|
+
*/
|
|
18
|
+
export function formatAppShellWallClock(
|
|
19
|
+
now: Date,
|
|
20
|
+
cfg: Record<string, unknown> | undefined,
|
|
21
|
+
lang: Lang,
|
|
22
|
+
): AppShellWallClockParts {
|
|
23
|
+
const timeZone = readDashboardTimeZoneFromCfg(cfg);
|
|
24
|
+
const use24Hour = readDashboardUse24HourClockFromCfg(cfg);
|
|
25
|
+
const locale = intlLocale(lang);
|
|
26
|
+
let display: string;
|
|
27
|
+
let ariaDatetime: string;
|
|
28
|
+
try {
|
|
29
|
+
display = new Intl.DateTimeFormat(locale, {
|
|
30
|
+
timeZone,
|
|
31
|
+
hour: "2-digit",
|
|
32
|
+
minute: "2-digit",
|
|
33
|
+
second: "2-digit",
|
|
34
|
+
hour12: !use24Hour,
|
|
35
|
+
}).format(now);
|
|
36
|
+
ariaDatetime = new Intl.DateTimeFormat(locale, {
|
|
37
|
+
timeZone,
|
|
38
|
+
weekday: "long",
|
|
39
|
+
year: "numeric",
|
|
40
|
+
month: "long",
|
|
41
|
+
day: "numeric",
|
|
42
|
+
hour: "2-digit",
|
|
43
|
+
minute: "2-digit",
|
|
44
|
+
second: "2-digit",
|
|
45
|
+
hour12: !use24Hour,
|
|
46
|
+
}).format(now);
|
|
47
|
+
} catch {
|
|
48
|
+
display = new Intl.DateTimeFormat(locale, {
|
|
49
|
+
hour: "2-digit",
|
|
50
|
+
minute: "2-digit",
|
|
51
|
+
second: "2-digit",
|
|
52
|
+
hour12: !use24Hour,
|
|
53
|
+
}).format(now);
|
|
54
|
+
ariaDatetime = new Intl.DateTimeFormat(locale, {
|
|
55
|
+
weekday: "long",
|
|
56
|
+
year: "numeric",
|
|
57
|
+
month: "long",
|
|
58
|
+
day: "numeric",
|
|
59
|
+
hour: "2-digit",
|
|
60
|
+
minute: "2-digit",
|
|
61
|
+
second: "2-digit",
|
|
62
|
+
hour12: !use24Hour,
|
|
63
|
+
}).format(now);
|
|
64
|
+
}
|
|
65
|
+
return { timeZone, display, ariaDatetime };
|
|
66
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DEFAULT_DASHBOARD_TIME_ZONE,
|
|
3
|
+
isValidIanaTimeZone,
|
|
4
|
+
} from "@/lib/dashboardTimeZone";
|
|
5
|
+
|
|
6
|
+
export const SESSION_NAME_TEMPLATE_MAX_LEN = 500;
|
|
7
|
+
|
|
8
|
+
/** Nombre maximal de caractères stockés dans `cfg.dashboardDefaultSessionNameTemplate`. */
|
|
9
|
+
export const SESSION_NAME_TEMPLATE_CFG_MAX_LEN = 400;
|
|
10
|
+
|
|
11
|
+
/** Composantes calendaires / horaires pour le fuseau IANA (`dashboardDisplayTimeZone`). */
|
|
12
|
+
export type ZonedDateTimeAtoms = {
|
|
13
|
+
year: string;
|
|
14
|
+
month: string;
|
|
15
|
+
day: string;
|
|
16
|
+
hour: string;
|
|
17
|
+
minute: string;
|
|
18
|
+
second: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function formatSessionNameAtoms(
|
|
22
|
+
atMs: number,
|
|
23
|
+
timeZone: string | undefined | null,
|
|
24
|
+
): ZonedDateTimeAtoms {
|
|
25
|
+
const tzRaw = typeof timeZone === "string" ? timeZone.trim() : "";
|
|
26
|
+
const tz =
|
|
27
|
+
tzRaw.length > 0 && isValidIanaTimeZone(tzRaw) ? tzRaw : DEFAULT_DASHBOARD_TIME_ZONE;
|
|
28
|
+
const d = new Date(atMs);
|
|
29
|
+
const dateParts = new Intl.DateTimeFormat("en-CA", {
|
|
30
|
+
timeZone: tz,
|
|
31
|
+
year: "numeric",
|
|
32
|
+
month: "2-digit",
|
|
33
|
+
day: "2-digit",
|
|
34
|
+
}).formatToParts(d);
|
|
35
|
+
const timeParts = new Intl.DateTimeFormat("en-GB", {
|
|
36
|
+
timeZone: tz,
|
|
37
|
+
hour: "2-digit",
|
|
38
|
+
minute: "2-digit",
|
|
39
|
+
second: "2-digit",
|
|
40
|
+
hour12: false,
|
|
41
|
+
hourCycle: "h23",
|
|
42
|
+
}).formatToParts(d);
|
|
43
|
+
|
|
44
|
+
const pick = (parts: Intl.DateTimeFormatPart[], type: Intl.DateTimeFormatPartTypes): string =>
|
|
45
|
+
parts.find((p) => p.type === type)?.value ?? "";
|
|
46
|
+
|
|
47
|
+
return {
|
|
48
|
+
year: pick(dateParts, "year"),
|
|
49
|
+
month: pick(dateParts, "month"),
|
|
50
|
+
day: pick(dateParts, "day"),
|
|
51
|
+
hour: pick(timeParts, "hour"),
|
|
52
|
+
minute: pick(timeParts, "minute"),
|
|
53
|
+
second: pick(timeParts, "second"),
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type FormatSessionNameTemplateOpts = Readonly<{
|
|
58
|
+
sessionId: string;
|
|
59
|
+
atMs: number;
|
|
60
|
+
timeZone: string | undefined | null;
|
|
61
|
+
}>;
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Remplace `%UUID` et un sous-ensemble inspiré du `strftime()` POSIX (IEEE / Open Group)
|
|
65
|
+
* pour composer le nom affiché. Le fuseau appliqué est celui des paramètres d’affichage
|
|
66
|
+
* (identique au tableau de bord / rapports pour les dates agrégées).
|
|
67
|
+
*
|
|
68
|
+
* Séquences :
|
|
69
|
+
* - `%%` → `%`
|
|
70
|
+
* - `%UUID` → identifiant complet de session (voir action `newSession`)
|
|
71
|
+
* - `%Y` `%m` `%d` `%H` `%M` `%S` : année quatre chiffres, mois, jour (00–31), heure 24 h, minute, seconde
|
|
72
|
+
* - `%y` deux derniers chiffres de `%Y`
|
|
73
|
+
* - `%F` → `%Y-%m-%d` (comme sous GNU date)
|
|
74
|
+
*
|
|
75
|
+
* Séquence `%` suivie d’un caractère non reconnu : la paire reste littérale (ex. `%Z` inchangé).
|
|
76
|
+
*
|
|
77
|
+
* Référence officielle conversions date/heure POSIX :
|
|
78
|
+
* https://pubs.opengroup.org/onlinepubs/9699919799/functions/strftime.html
|
|
79
|
+
*/
|
|
80
|
+
function strftimeSubstitution(spec: string, z: ZonedDateTimeAtoms): string | undefined {
|
|
81
|
+
switch (spec) {
|
|
82
|
+
case "Y":
|
|
83
|
+
return z.year;
|
|
84
|
+
case "y":
|
|
85
|
+
return z.year.length >= 2 ? z.year.slice(-2) : z.year;
|
|
86
|
+
case "m":
|
|
87
|
+
return z.month;
|
|
88
|
+
case "d":
|
|
89
|
+
return z.day;
|
|
90
|
+
case "H":
|
|
91
|
+
return z.hour;
|
|
92
|
+
case "M":
|
|
93
|
+
return z.minute;
|
|
94
|
+
case "S":
|
|
95
|
+
return z.second;
|
|
96
|
+
case "F":
|
|
97
|
+
return `${z.year}-${z.month}-${z.day}`;
|
|
98
|
+
default:
|
|
99
|
+
return undefined;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function formatSessionNameTemplate(
|
|
104
|
+
template: string,
|
|
105
|
+
opts: FormatSessionNameTemplateOpts,
|
|
106
|
+
): string {
|
|
107
|
+
const z = formatSessionNameAtoms(opts.atMs, opts.timeZone);
|
|
108
|
+
let out = "";
|
|
109
|
+
let i = 0;
|
|
110
|
+
while (i < template.length) {
|
|
111
|
+
const c = template[i];
|
|
112
|
+
if (c !== "%") {
|
|
113
|
+
out += c;
|
|
114
|
+
i += 1;
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const j = i + 1;
|
|
118
|
+
if (j >= template.length) {
|
|
119
|
+
out += "%";
|
|
120
|
+
break;
|
|
121
|
+
}
|
|
122
|
+
if (template[j] === "%") {
|
|
123
|
+
out += "%";
|
|
124
|
+
i = j + 1;
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
if (template.slice(j).startsWith("UUID")) {
|
|
128
|
+
out += opts.sessionId;
|
|
129
|
+
i = j + "UUID".length;
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const spec = template.slice(j, j + 1);
|
|
133
|
+
if (!spec) {
|
|
134
|
+
break;
|
|
135
|
+
}
|
|
136
|
+
i = j + 1;
|
|
137
|
+
const sub = strftimeSubstitution(spec, z);
|
|
138
|
+
out += sub ?? `%${spec}`;
|
|
139
|
+
}
|
|
140
|
+
return out.trim();
|
|
141
|
+
}
|
|
@@ -6,21 +6,192 @@ export type UserChangelogEntry = {
|
|
|
6
6
|
|
|
7
7
|
export const USER_CHANGELOG_ENTRIES: UserChangelogEntry[] = [
|
|
8
8
|
{
|
|
9
|
-
"version": "
|
|
9
|
+
"version": "1.0.0-beta.21",
|
|
10
10
|
"items": [
|
|
11
|
-
"
|
|
11
|
+
"**Parsing** : `findProjectTokens` (`lib/taskParsing.ts`) reconnaît désormais **`!projet`** et **`!projet#sous-étiquette`** lorsque le **`!`** suit **directement** un caractère autorisé (sans espace avant le sigil), sur le même principe que les **`#étiquettes`** « collées » au mot précédent. Les jetons **`@`** restent limités à une **frontière début de chaîne ou espace** afin d’éviter les faux positifs (ex. adresses courriel du type `contact@domaine`).",
|
|
12
|
+
"**Effet** : la modification d’un libellé du type `Courrier!perso#dentiste` met correctement à jour **`project`**, **`personalProject`** et les **étiquettes** persistées (y compris via **`updateTask`**), ce qui aligne le **reporting** (étiquettes, ventilation par projet selon le scope) sur l’intention utilisateur.",
|
|
13
|
+
"**Guide utilisateur** : `lib/userGuideCopy.ts` et `docs/USER_GUIDE.md` (FR/EN) — précision sur l’espace optionnel avant **`!`** ; en-tête et exemples de livraison pointent **`1.0.0-beta.21`**.",
|
|
14
|
+
"**Implémentation** : `docs/IMPLEMENTATION_DETAILS.md` (règle de frontière `!` / `@`) ; `lib/implementationNotes.ts` (version affichée et détail parsing scoped `!`).",
|
|
15
|
+
"**Dépôt** : `README.md` (référence de version npm).",
|
|
16
|
+
"**Tests** : `lib/taskParsing.test.ts` — cas `Courrier!perso#dentiste` ; attentes **`personalProject`** pour `parseQuickAddTagOrProjectLine` / `buildStartTaskFromInput` lorsqu’un projet est présent.",
|
|
17
|
+
"Bump applicatif : **`1.0.0-beta.21`** (`package.json`, `package-lock.json`) ; **CHANGELOG** usager régénéré (`npm run changelog:build`)."
|
|
12
18
|
]
|
|
13
19
|
},
|
|
14
20
|
{
|
|
15
|
-
"version": "
|
|
21
|
+
"version": "1.0.0-beta.20",
|
|
16
22
|
"items": [
|
|
17
|
-
"
|
|
23
|
+
"**Dépendances runtime** : `@types/node`, `@types/react` et `@types/react-dom` passées en **`dependencies`** avec `typescript` — Next.js 16 exige ces paquets pour la phase « Running TypeScript » du `next build` ; une installation **`npm install --omit=dev`** ne les omet plus (évite le message *It looks like you're trying to use TypeScript…* hors environnement de développement complet).",
|
|
24
|
+
"**Contenu du tarball publié** : suppression du champ **`files`** dans `package.json` (il neutralisait **`.npmignore`**, comportement **npm-packlist**) ; filtrage du paquet via **`.npmignore`** (fichiers `*.test.*` / `*.spec.*`, dossiers `e2e/`, `test/`, `docs/`, outils CI, etc.) en conservant les sources nécessaires au build.",
|
|
25
|
+
"Bump applicatif : **`1.0.0-beta.20`** (`package.json`, `package-lock.json`) ; **CHANGELOG** usager régénéré (`npm run changelog:build`)."
|
|
18
26
|
]
|
|
19
27
|
},
|
|
20
28
|
{
|
|
21
|
-
"version": "1.0.0",
|
|
29
|
+
"version": "1.0.0-beta.19",
|
|
22
30
|
"items": [
|
|
23
|
-
"
|
|
31
|
+
"**Affichage planifié** : regroupement sous **Planifiées**, styles distincts (section, fond, point d’état) ; **suppression des pastilles texte** « planifié » sur les cartes tâche et les lignes de session. Tâche **non terminée** dont le **début** (`startTime`) est encore **futur** : pas de minuteur « en direct ». Tâche **terminée** : regroupement planifié seulement si **début et fin** parseables sont **tous deux** strictement futurs — une **fin** future seule ne retient plus la carte sous Planifiées lorsque le début est déjà passé (`isTaskDisplayPlanned`). Session live à **startAt** futur : ligne distincte dans la liste ; décompte planifiées sur la carte session ; tiroir AppShell sans ces entrées comme suivi actif.",
|
|
32
|
+
"**Frontière planifié → en cours** : lorsqu’une tâche rejoint les minuteurs **en cours** alors qu’**au moins un** autre minuteur était déjà visible comme en cours, une **modale** (pause / terminer / parallèle, même mémorisation que le démarrage manuel) s’applique aux tâches déjà au minuteur ; hors route `/`, l’icône **Tableau de bord** peut **pulser**.",
|
|
33
|
+
"**Modales globales** : correction d’un décalage de positionnement lors des transitions de route ; l’animation de conteneur ne transporte plus de `transform`, ce qui rétablit l’ancrage viewport des modales en `position: fixed`.",
|
|
34
|
+
"**Horloge murale** permanente dans la barre d’outils : fuseau IANA et format 12/24 h des paramètres **Tableau de bord web**, tick chaque seconde ; correctif d’**hydratation** SSR/CSR sur les secondes au premier rendu.",
|
|
35
|
+
"**Sélecteurs date/heure** : bouton **crochet** pour valider et fermer le calendrier ; préremplissage **début** avec minutes `:00` (bouton **Today** idem) ; **Now** à l’instant exact ; champs de **fin** arrondis à la **demi-heure** suivante (**Today** inclus).",
|
|
36
|
+
"**Heure de fin planifiée** (tâche ou session) : clignotement doux entre 15 et 5 minutes restantes, puis plus rapide jusqu’à 0.",
|
|
37
|
+
"**Colonne tâches** : suppression d’une troncature visuelle pendant l’animation d’ouverture ; défilement correct sur les listes longues.",
|
|
38
|
+
"**Installation desktop / PWA** : priorité **512x512** + rôle `maskable` pour l’icône installée.",
|
|
39
|
+
"**Jeton `!`** : en parallèle de `@projet`, le libellé de tâche accepte **`!projet`** et les étiquettes sous portée **`!projet#sous-étiquette`** ; le moteur distingue **`personalProject`** sur les tâches et maintient **`knownPersonalProjects`** sur le payload (liste distincte de `knownProjects`).",
|
|
40
|
+
"**Actions** : `startTask`, `addHistoricalTask`, `updateTask`, `saveTaskTemplate` ; renommage serveur avec **`renameProjectMetadata`** et **`personalOnly`** (pas encore d’inline Paramètres dédié aux listes `!`).",
|
|
41
|
+
"**UI** : pastilles et suggestions **rose** pour le personnel (`taskFieldStyles`, `TaskFocusPanel`, `TaskSessionLiveCard`, `SavedProjectPicker`, `TagPills`) ; **Gantt** d’une tâche : barres et **totaux séparés** temps productif (`@`) vs personnel (`!`).",
|
|
42
|
+
"**Rapports** : les grilles « par projet » utilisent par défaut le scope **`work`** (`aggregateProjectTaskMinutesByDay`) — le temps `!` n’y est pas mélangé ; les scopes **`personal`** / **`all`** existent dans `lib/reportingAggregate.ts` pour une future vue UI dédiée.",
|
|
43
|
+
"**Qualité** : tests unitaires sur les modèles avec projet personnel (`lib/taskTemplateDraft.test.ts`) ; pas de parcours E2E Playwright dédié aux jetons `!` dans cette version.",
|
|
44
|
+
"**Guide utilisateur** : `lib/userGuideCopy.ts` et `docs/USER_GUIDE.md` (FR/EN) — convention **`#` / `@` / `!`**, rapports (grilles projet `@` sans mélanger `!`), limite actuelle côté Paramètres pour les projets personnels.",
|
|
45
|
+
"**Implémentation** : `lib/implementationNotes.ts` (user stories et coches intégration/E2E) ; **`docs/IMPLEMENTATION_DETAILS.md`** — section projets personnels et scopes reporting.",
|
|
46
|
+
"**Changelog usager** : régénération via `npm run changelog:build` (`lib/generatedUserChangelog.ts`).",
|
|
47
|
+
"**Paquet npm** : version **`1.0.0-beta.19`** ; script **`prepublishOnly`** = `npm run build` avant `npm publish` sur `@nightkatana/kronosys-app`."
|
|
48
|
+
]
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"version": "1.0.0-beta.18",
|
|
52
|
+
"items": [
|
|
53
|
+
"Nouveautés — Sauvegarde/restauration: ajout de l’API `POST /api/restore` (import `json` ou `sqlite`) avec couverture d’intégration dédiée.",
|
|
54
|
+
"Nouveautés — User stories in-app: ajout de la page **Implémentation** (`/implementation`) avec inventaire exhaustif ✓/✗, checklist locale « revue » et accès direct au changelog usager.",
|
|
55
|
+
"Nouveautés — Modèles de tâche: section dédiée dans Paramètres, création depuis une tâche, suggestions « Modèle : » dans la saisie, disponibilité dans la palette **Spotlight**.",
|
|
56
|
+
"Nouveautés — Vue **Gantt du jour**: modal timeline globale des intervalles de tâches sur les sessions de la journée.",
|
|
57
|
+
"Nouveautés — Pause globale contextuelle: modal de confirmation enrichie avec résumé détaillé (murale, temps actifs/codage, compteurs tâches/sous-tâches, ventilation minuteurs).",
|
|
58
|
+
"Nouveautés — Nom de session par défaut: gabarit configurable (`%UUID` + tokens `strftime`) appliqué à la création de session.",
|
|
59
|
+
"Améliorations — Tableau de bord: en-tête AppShell consolidé (métadonnées de session, placeholder commande, navigation), transitions de route et cohérence inter-pages accrues.",
|
|
60
|
+
"Améliorations — Reporting: ajout du temps non concurrent par jour, ventilation des sessions par type de clôture, aides métriques et précision accrue des agrégats.",
|
|
61
|
+
"Améliorations — Sessions/tâches: consolidation des statistiques latérales, gestion des notes et meilleure continuité live/historique.",
|
|
62
|
+
"Améliorations — Paramètres: évolution des sections Étiquettes/Projets/Modèles et meilleure ergonomie des flux de configuration.",
|
|
63
|
+
"Qualité — Tests: renforcement substantiel de la couverture (unitaires + intégration + E2E) sur restore API, templates, Gantt, pause globale, agrégats reporting et flux session/tâche.",
|
|
64
|
+
"Documentation — Guide utilisateur synchronisé pour `beta.18` sur les deux surfaces (`/guide` et `docs/USER_GUIDE.md`), Spotlight harmonisé, page `/implementation` et `docs/IMPLEMENTATION_DETAILS.md` alignés.",
|
|
65
|
+
"Maintenance — Bump applicatif vers `1.0.0-beta.18` (`package.json`, `package-lock.json`) et régénération du changelog usager (`lib/generatedUserChangelog.ts`)."
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"version": "1.0.0-beta.17",
|
|
70
|
+
"items": [
|
|
71
|
+
"Nouveautés — Notes libres: ajout d’une **note de tâche** et d’une **note de session** (texte libre) pour documenter le contexte de travail directement sur l’activité concernée.",
|
|
72
|
+
"Améliorations — Tableau de bord: la note de tâche est affichée **avant** la liste des sous-tâches pour garder le contexte visible pendant la décomposition.",
|
|
73
|
+
"Améliorations — Sessions et archives: aperçu de note de session (icône + extrait) dans la liste des sessions et dans la liste des sessions archivées.",
|
|
74
|
+
"Qualité — Tests: ajout de tests unitaires UI pour l’ordre d’affichage note/sous-tâches et la présence de l’aperçu de note en liste de sessions, en plus des tests serveur sur la persistance des notes.",
|
|
75
|
+
"Documentation — Mise à jour du guide utilisateur (in-app + dépôt) et des détails d’implémentation pour les notes de tâches/sessions.",
|
|
76
|
+
"Nouveautés — Planification d’heure de fin sur les **tâches en cours** : définir une heure de fin programme l’échéance et met automatiquement le minuteur en pause lorsqu’elle est atteinte.",
|
|
77
|
+
"Nouveautés — Planification d’heure de fin sur la **session live** : définir une heure de fin agit comme échéance de session et met automatiquement en pause la durée murale à l’instant prévu.",
|
|
78
|
+
"Améliorations — Tableau de bord : le sélecteur **Fin** est disponible aussi sur tâches/sessions en cours (plus seulement sur les éléments déjà terminés), avec affichage de l’échéance planifiée.",
|
|
79
|
+
"Qualité — Tests : renforcement de la couverture (dispatch + hydratation minuteurs/session) pour les scénarios planifiés et les cas d’échéance dépassée.",
|
|
80
|
+
"Documentation — Guide utilisateur et détails d’implémentation mis à jour pour expliciter la différence « fin réelle » vs « fin planifiée (auto-pause) ».",
|
|
81
|
+
"Nouveautés — Étiquettes et projets : renommage **inline** dans la colonne droite du tableau de bord (édition en place, validation **Entrée**, annulation **Échap**).",
|
|
82
|
+
"Améliorations — Confirmation de renommage : avertissement explicite sur l’impact reporting + option « ne plus afficher » mémorisée localement.",
|
|
83
|
+
"Qualité — UX : suppression des ambiguïtés d’actions (pas de bouton de sauvegarde en double pendant le renommage inline)."
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"version": "1.0.0-beta.16",
|
|
88
|
+
"items": [
|
|
89
|
+
"Nouveautés — Page Implémentation (`/implementation`) avec inventaire exhaustif des user stories (✓ livré · ✗ absent/hors périmètre), checklist locale « revue » et accès direct au changelog usager.",
|
|
90
|
+
"Nouveautés — Documentation technique d’implémentation ajoutée dans `docs/IMPLEMENTATION_DETAILS.md` (modèle temps session murale vs minuteurs de tâches, pauses, reporting, architecture et maintenance).",
|
|
91
|
+
"Nouveautés — Modèles de tâche: sauvegarde depuis une tâche, suggestions « Modèle: » dans la saisie, présence dans la palette `Ctrl+K`, gestion dédiée dans Paramètres.",
|
|
92
|
+
"Nouveautés — Pause globale contextuelle avec modale de confirmation et aperçu détaillé (murale, temps actif/codage, compteurs tâches/sous-tâches, ventilation des minuteurs).",
|
|
93
|
+
"Nouveautés — Vue Gantt du jour: frise des intervalles de tâches sur l’ensemble des sessions de la journée.",
|
|
94
|
+
"Nouveautés — Nom de session par défaut avec gabarit configurable (`%UUID` + tokens `strftime`) dans les paramètres.",
|
|
95
|
+
"Nouveautés — Restauration locale via `POST /api/restore` (`json` ou `sqlite`) en complément de `GET /api/backup`.",
|
|
96
|
+
"Améliorations — Reporting: ajout du temps non concurrent par jour, ventilation des sessions par type de clôture, métriques d’assiduité quand une heure prévue est fournie.",
|
|
97
|
+
"Améliorations — Spotlight: la palette de recherche rapide est désormais explicitement nommée « Spotlight » dans l’interface.",
|
|
98
|
+
"Qualité — Tests: ajout/renforcement des parcours E2E et tests unitaires (implémentation, templates, pause globale, restauration API, agrégats).",
|
|
99
|
+
"Documentation — Guide utilisateur: mise à jour complète in-app + dépôt, et alignement des chaînes FR/EN (dashboard, reporting, paramètres, licences, implémentation)."
|
|
100
|
+
]
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
"version": "1.0.0-beta.15",
|
|
104
|
+
"items": [
|
|
105
|
+
"Ajout de la commande de version CLI: `kronosys --version` affiche désormais la version installée.",
|
|
106
|
+
"Ajout des alias `kronosys -v` et `kronosys version` pour afficher la version installée."
|
|
107
|
+
]
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
"version": "1.0.0-beta.14",
|
|
111
|
+
"items": [
|
|
112
|
+
"Ajout de la page **Implémentation** (`/implementation`) avec inventaire exhaustif des user stories livrées ou hors périmètre.",
|
|
113
|
+
"Ajout des **modèles de tâche** (création depuis une tâche, gestion dans Paramètres, recherche rapide Ctrl+K).",
|
|
114
|
+
"Ajout d’une **pause globale contextuelle** avec modale de confirmation et aperçu des durées associées.",
|
|
115
|
+
"Ajout de la vue **Gantt du jour** pour visualiser les intervalles de tâches de la session affichée."
|
|
116
|
+
]
|
|
117
|
+
},
|
|
118
|
+
{
|
|
119
|
+
"version": "1.0.0-beta.13",
|
|
120
|
+
"items": [
|
|
121
|
+
"Version intermédiaire de stabilisation (pas de nouveauté utilisateur majeure documentée)."
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"version": "1.0.0-beta.12",
|
|
126
|
+
"items": [
|
|
127
|
+
"Correctif du runtime CLI: installation des dépendances avec `--ignore-scripts` pour éviter les erreurs Husky hors dépôt."
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
"version": "1.0.0-beta.11",
|
|
132
|
+
"items": [
|
|
133
|
+
"Correctif Windows du lanceur CLI (PowerShell + installation runtime locale).",
|
|
134
|
+
"Rebuild explicite de `better-sqlite3` dans le runtime local."
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
"version": "1.0.0-beta.10",
|
|
139
|
+
"items": [
|
|
140
|
+
"Correctif de dispatch des commandes Windows pour le runtime CLI."
|
|
141
|
+
]
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
"version": "1.0.0-beta.9",
|
|
145
|
+
"items": [
|
|
146
|
+
"Correctif de quoting des commandes Windows dans le lanceur CLI."
|
|
147
|
+
]
|
|
148
|
+
},
|
|
149
|
+
{
|
|
150
|
+
"version": "1.0.0-beta.8",
|
|
151
|
+
"items": [
|
|
152
|
+
"Correctif initial Windows du launcher (`npm.cmd`/`npx.cmd`)."
|
|
153
|
+
]
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
"version": "1.0.0-beta.7",
|
|
157
|
+
"items": [
|
|
158
|
+
"Stabilisation du packaging bêta et génération des archives de distribution."
|
|
159
|
+
]
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
"version": "1.0.0-beta.6",
|
|
163
|
+
"items": [
|
|
164
|
+
"Correctif de résolution d’alias d’import (`@/...`) pour le build de production."
|
|
165
|
+
]
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"version": "1.0.0-beta.5",
|
|
169
|
+
"items": [
|
|
170
|
+
"Retour temporaire au mode build à la première exécution pour améliorer la compatibilité multiplateforme."
|
|
171
|
+
]
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
"version": "1.0.0-beta.4",
|
|
175
|
+
"items": [
|
|
176
|
+
"Ajustements de packaging et de lancement CLI pour la série bêta."
|
|
177
|
+
]
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
"version": "1.0.0-beta.2",
|
|
181
|
+
"items": [
|
|
182
|
+
"Correctif des dépendances de build nécessaires au runtime CLI."
|
|
183
|
+
]
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"version": "1.0.0-beta.1",
|
|
187
|
+
"items": [
|
|
188
|
+
"`kronosys` lance maintenant le build de production automatiquement si `.next` est absent."
|
|
189
|
+
]
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
"version": "1.0.0-beta.0",
|
|
193
|
+
"items": [
|
|
194
|
+
"Première version bêta publiée."
|
|
24
195
|
]
|
|
25
196
|
},
|
|
26
197
|
{
|