@nbtca/prompt 1.5.10 → 1.5.11

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 CHANGED
@@ -51,8 +51,6 @@ npm run check
51
51
  `check` runs formatting, lint, full TypeScript validation, tests, build, package
52
52
  consumer checks and dependency audit.
53
53
 
54
- Project guides and release notes live in the [Wiki](https://github.com/nbtca/Prompt/wiki).
55
-
56
54
  ## License
57
55
 
58
56
  MIT
@@ -5,9 +5,9 @@ import { padEndV, visualWidth, wrapAnsiWithIndent } from '../../core/text.js';
5
5
  import { peekNextClassLine, peekTodayLines, peekWeekAheadInfo, peekUnresolvedCount, } from '../../features/schedule-view.js';
6
6
  import { loadCalendarOrThrow, peekCalendar, toDisplayEvent, renderEventBrief, } from '../../features/calendar.js';
7
7
  import { weekdayShortLabel } from '../../features/schedule-render.js';
8
- import { addLocalDays } from '../../core/calendar-day.js';
9
8
  import { passiveFooterHint } from '../chrome.js';
10
- import { campusWeekday } from '@nbtca/nbtcal/timetable';
9
+ import { campusDateTime, campusIsoDate } from '@nbtca/nbtcal/timetable';
10
+ import { isoDayDifference, localDayDifference, parseLocalDate } from '../../core/calendar-day.js';
11
11
  import { loadingLines } from '../../core/components/spinner.js';
12
12
  const WEEKDAYS = [1, 2, 3, 4, 5, 6, 7];
13
13
  function wrappedIndentedLines(label, cols, style) {
@@ -20,10 +20,11 @@ function wrappedRenderedLines(line, cols) {
20
20
  const content = line.startsWith(space.indent) ? line.slice(space.indent.length) : line;
21
21
  return wrappedIndentedLines(content, cols, (value) => value);
22
22
  }
23
+ const DAY_MS = 86_400_000;
23
24
  const DAY_PROGRESS_WIDTH = 20;
24
25
  function renderDayProgress(now, cols) {
25
- const minutesElapsed = now.getHours() * 60 + now.getMinutes();
26
- const fraction = Math.min(1, Math.max(0, minutesElapsed / 1440));
26
+ const midnight = campusDateTime(campusIsoDate(now), '00:00');
27
+ const fraction = Math.min(1, Math.max(0, (now.getTime() - midnight.getTime()) / DAY_MS));
27
28
  const pct = Math.round(fraction * 100);
28
29
  const percentage = `${pct}%`;
29
30
  const width = Number.isFinite(cols) ? Math.max(1, Math.floor(cols)) : Number.POSITIVE_INFINITY;
@@ -149,8 +150,14 @@ function calendarSnapshot(cal, weekAheadInfo) {
149
150
  .map((event) => renderEventBrief(toDisplayEvent(event), now));
150
151
  if (!weekAheadInfo)
151
152
  return { eventLines };
152
- const weekEnd = addLocalDays(weekAheadInfo.weekStartDate, 7);
153
- const daySet = new Set(cal.inRange(weekAheadInfo.weekStartDate, weekEnd).map((event) => campusWeekday(event.start)));
153
+ const { weekStart } = weekAheadInfo;
154
+ const monday = campusDateTime(weekStart, '00:00');
155
+ const daySet = new Set(cal
156
+ .inRange(new Date(monday.getTime() - DAY_MS), new Date(monday.getTime() + 8 * DAY_MS))
157
+ .map((event) => 1 +
158
+ (event.isAllDay
159
+ ? localDayDifference(parseLocalDate(weekStart), event.start)
160
+ : isoDayDifference(weekStart, campusIsoDate(event.start)))));
154
161
  return {
155
162
  eventLines,
156
163
  weekAhead: {
@@ -1,11 +1,11 @@
1
- import { createTimetableSchedule, } from '@nbtca/nbtcal/timetable';
1
+ import { campusIsoDate, createTimetableSchedule, } from '@nbtca/nbtcal/timetable';
2
2
  import { c, type, space, glyph } from '../../core/theme.js';
3
3
  import { pickIcon } from '../../core/icons.js';
4
4
  import { t, fmt } from '../../i18n/index.js';
5
5
  import { renderListFieldWithContext } from '../fields/list-field.js';
6
6
  import { renderNextClassBanner, renderWeekGrid, renderUnresolvedItems, renderTodayTimeline, weekdayShortLabel, renderTermDensity, renderMeetingDetail, renderDayTimeline, renderDaySwitcher, } from '../../features/schedule-render.js';
7
7
  import { sanitizeTerminalLine, visualWidth, wrapAnsiWithIndent } from '../../core/text.js';
8
- import { localDayDifference, parseLocalDate, parseLocalMonday } from '../../core/calendar-day.js';
8
+ import { isoDayDifference } from '../../core/calendar-day.js';
9
9
  import { loadingLines } from '../../core/components/spinner.js';
10
10
  function heading(label) {
11
11
  return `${space.indent}${type.heading(label)}`;
@@ -90,7 +90,7 @@ function hubPreGridLines(state, now, cols) {
90
90
  lines.push(heading(trans.timetable.termNotStarted));
91
91
  lines.push(hint(fmt(trans.timetable.termStartsIn, {
92
92
  date: state.weekOne,
93
- days: String(daysBetween(now, new Date(`${state.weekOne}T00:00:00`))),
93
+ days: String(daysUntil(now, state.weekOne)),
94
94
  })));
95
95
  lines.push('');
96
96
  lines.push(heading(trans.timetable.termPreviewWeek));
@@ -160,7 +160,7 @@ const TERM_PROGRESS_WIDTH = 20;
160
160
  function renderTermProgressBar(w, cols) {
161
161
  if (!w.nextBreakStart)
162
162
  return null;
163
- const totalWeeks = Math.max(1, Math.round(localDayDifference(parseLocalMonday(w.weekOneMonday), parseLocalDate(w.nextBreakStart)) / 7));
163
+ const totalWeeks = Math.max(1, Math.round(isoDayDifference(w.weekOneMonday, w.nextBreakStart) / 7));
164
164
  const currentWeek = w.currentWeek;
165
165
  const labelText = fmt(t().timetable.weekLabel2, { week: `${currentWeek}/${totalWeeks}` });
166
166
  const label = type.hint(labelText);
@@ -177,8 +177,8 @@ function renderTermProgressBar(w, cols) {
177
177
  ? [`${indent}${bar} ${label}`]
178
178
  : [`${indent}${bar}`, ...hintLines(labelText, cols)];
179
179
  }
180
- function daysBetween(a, b) {
181
- return Math.max(0, localDayDifference(a, b));
180
+ function daysUntil(now, date) {
181
+ return Math.max(0, isoDayDifference(campusIsoDate(now), date));
182
182
  }
183
183
  function renderPublicBody(state, now, bodyRows, cols) {
184
184
  const trans = t();
@@ -202,7 +202,7 @@ function renderPublicBody(state, now, bodyRows, cols) {
202
202
  if (w.nextBreakStart && w.nextBreakTitle) {
203
203
  lines.push(...hintLines(fmt(trans.timetable.daysUntilBreak, {
204
204
  title: sanitizeTerminalLine(w.nextBreakTitle),
205
- days: String(daysBetween(now, parseLocalDate(w.nextBreakStart))),
205
+ days: String(daysUntil(now, w.nextBreakStart)),
206
206
  }), cols));
207
207
  }
208
208
  }
@@ -35,3 +35,6 @@ function localDayIndex(date) {
35
35
  export function localDayDifference(start, end) {
36
36
  return localDayIndex(end) - localDayIndex(start);
37
37
  }
38
+ export function isoDayDifference(start, end) {
39
+ return (Date.parse(end) - Date.parse(start)) / DAY_MS;
40
+ }
@@ -1,4 +1,4 @@
1
- import { createTimetableSchedule } from '@nbtca/nbtcal/timetable';
1
+ import { campusDateTime, campusIsoDate, createTimetableSchedule } from '@nbtca/nbtcal/timetable';
2
2
  import { countdownParts, isCountdownUrgent } from './calendar-query.js';
3
3
  import { c, type, space, glyph } from '../core/theme.js';
4
4
  import { pickIcon } from '../core/icons.js';
@@ -64,25 +64,6 @@ export function renderNextClassBanner(next, now, cols = Number.POSITIVE_INFINITY
64
64
  }
65
65
  return styleWhen(compactWhen);
66
66
  }
67
- export function renderTodayClasses(meetings, periods, now) {
68
- const trans = t();
69
- const sorted = [...meetings].sort((a, b) => a.startPeriod - b.startPeriod);
70
- if (sorted.length === 0)
71
- return `${space.indent}${type.hint(trans.timetable.noClassToday)}`;
72
- const dot = pickIcon('·', '-');
73
- const marker = pickIcon('▸', '>');
74
- const lines = sorted.map((m) => {
75
- const time = span(m, periods);
76
- const startStr = periods.find((p) => p.period === m.startPeriod)?.start ?? '00:00';
77
- const endStr = periods.find((p) => p.period === m.endPeriod)?.end ?? '23:59';
78
- const nowStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
79
- const live = nowStr >= startStr && nowStr <= endStr;
80
- const head = live ? `${type.active(marker)} ` : ' ';
81
- const loc = m.location ? ` ${dot} ${type.hint(m.location)}` : '';
82
- return `${space.indent}${head}${type.hint(time)} ${live ? type.active(m.courseName) : type.body(m.courseName)}${loc}`;
83
- });
84
- return lines.join('\n');
85
- }
86
67
  export function weekdayShortLabel(wd) {
87
68
  const trans = t();
88
69
  const labels = [
@@ -101,7 +82,8 @@ function renderTimeline(meetings, periods, now, isToday, alwaysShowLocation, cur
101
82
  const sorted = [...meetings].sort((a, b) => a.startPeriod - b.startPeriod);
102
83
  if (sorted.length === 0)
103
84
  return `${space.indent}${type.hint(trans.timetable.noClassToday)}`;
104
- const nowStr = `${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}`;
85
+ const today = campusIsoDate(now);
86
+ const minute = Math.floor(now.getTime() / 60_000) * 60_000;
105
87
  const dot = pickIcon('·', '-');
106
88
  const rule = pickIcon('─', '-');
107
89
  const midConnector = pickIcon('┼', '+');
@@ -110,8 +92,9 @@ function renderTimeline(meetings, periods, now, isToday, alwaysShowLocation, cur
110
92
  const lines = sorted.map((m, i) => {
111
93
  const startStr = periods.find((p) => p.period === m.startPeriod)?.start ?? '00:00';
112
94
  const endStr = periods.find((p) => p.period === m.endPeriod)?.end ?? '23:59';
113
- const isLive = isToday && nowStr >= startStr && nowStr <= endStr;
114
- const isDone = isToday && nowStr > endStr;
95
+ const end = campusDateTime(today, endStr);
96
+ const isLive = isToday && campusDateTime(today, startStr).getTime() <= minute && minute <= end.getTime();
97
+ const isDone = isToday && minute > end.getTime();
115
98
  const isCursor = cursorPeriod !== undefined && m.startPeriod <= cursorPeriod && cursorPeriod <= m.endPeriod;
116
99
  const connector = i === 0 ? topConnector : midConnector;
117
100
  const marker = isLive ? type.active(pickIcon('▶', '>')) : ' ';
@@ -124,9 +107,6 @@ function renderTimeline(meetings, periods, now, isToday, alwaysShowLocation, cur
124
107
  compactStatusText = statusText;
125
108
  }
126
109
  else if (isLive) {
127
- const end = new Date(now);
128
- const [eh, em] = endStr.split(':').map((x) => Number.parseInt(x, 10));
129
- end.setHours(eh !== undefined && Number.isFinite(eh) ? eh : 0, em !== undefined && Number.isFinite(em) ? em : 0, 0, 0);
130
110
  const remaining = countdownParts(end, now);
131
111
  const mins = remaining.days * 1440 + remaining.hours * 60 + remaining.minutes;
132
112
  statusText = `${trans.timetable.classLive} ${dot} ${fmt(trans.timetable.minutesRemaining, { minutes: String(mins) })}`;
@@ -1,9 +1,9 @@
1
- import { createTimetableSchedule } from '@nbtca/nbtcal/timetable';
2
- import { addLocalDays, parseLocalMonday } from '../core/calendar-day.js';
1
+ import { campusDateTime, campusIsoDate, createTimetableSchedule, } from '@nbtca/nbtcal/timetable';
3
2
  import { renderNextClassBanner, renderTodayTimeline } from './schedule-render.js';
4
3
  import { loadCurrentPointer, loadTimetableCache } from './schedule-store.js';
5
4
  import { sanitizeTimetable } from './timetable-sanitize.js';
6
5
  const WEEKDAYS = [1, 2, 3, 4, 5, 6, 7];
6
+ const WEEK_MS = 7 * 86_400_000;
7
7
  function loadCachedTimetable() {
8
8
  const pointer = loadCurrentPointer();
9
9
  if (!pointer)
@@ -69,8 +69,8 @@ export function peekWeekAheadInfo(now = new Date()) {
69
69
  if (week < 1)
70
70
  return null;
71
71
  const classDays = WEEKDAYS.map((weekday) => schedule.meetingsOnDay(week, weekday).length > 0);
72
- const weekStartDate = addLocalDays(parseLocalMonday(cached.weekOneMonday), (week - 1) * 7);
73
- return { weekStartDate, classDays };
72
+ const weekStart = campusDateTime(cached.weekOneMonday, '00:00').getTime() + (week - 1) * WEEK_MS;
73
+ return { weekStart: campusIsoDate(new Date(weekStart)), classDays };
74
74
  }
75
75
  catch {
76
76
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbtca/prompt",
3
- "version": "1.5.10",
3
+ "version": "1.5.11",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {
@@ -46,8 +46,8 @@
46
46
  "interactive"
47
47
  ],
48
48
  "dependencies": {
49
- "@nbtca/docs": "^0.3.0",
50
- "@nbtca/nbtcal": "^0.4.0",
49
+ "@nbtca/docs": "^0.3.1",
50
+ "@nbtca/nbtcal": "^0.4.1",
51
51
  "chalk": "^5.6.2",
52
52
  "cheerio": "1.0.0",
53
53
  "fetch-cookie": "^3.2.0",