@nbtca/prompt 1.5.1 → 1.5.3
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/dist/app/app.js +27 -3
- package/dist/app/chrome.js +13 -2
- package/dist/app/keys.js +9 -1
- package/dist/app/views/docs.js +5 -1
- package/dist/features/calendar.js +30 -9
- package/package.json +1 -1
package/dist/app/app.js
CHANGED
|
@@ -23,6 +23,7 @@ export async function runApp() {
|
|
|
23
23
|
let clockTimer;
|
|
24
24
|
let busyTimer;
|
|
25
25
|
let painted;
|
|
26
|
+
let lastBody = { length: 0, height: 0 };
|
|
26
27
|
const viewIds = getAppTabs().map((tab) => tab.id);
|
|
27
28
|
const nativeViews = {
|
|
28
29
|
home: homeView,
|
|
@@ -76,9 +77,11 @@ export async function runApp() {
|
|
|
76
77
|
const tabs = getAppTabs();
|
|
77
78
|
const chrome = resolveChromeLayout(rows);
|
|
78
79
|
const header = renderHeader(tabs, view, cols, chrome.headerLines);
|
|
79
|
-
const footer = renderFooter(view, cols, tabs.length, active?.footerHint?.(tabs.length, cols), chrome.footerLines);
|
|
80
80
|
const body = active?.render(ctx) ?? [];
|
|
81
81
|
const bodyScroll = active?.capturesInput?.() ? Number.MAX_SAFE_INTEGER : scroll;
|
|
82
|
+
const height = computeBodyRows(rows, chrome.headerLines, chrome.footerLines);
|
|
83
|
+
lastBody = { length: body.length, height };
|
|
84
|
+
const footer = renderFooter(view, cols, tabs.length, active?.footerHint?.(tabs.length, cols), chrome.footerLines, active?.scrollsBody?.() === true ? scrollPercent() : undefined);
|
|
82
85
|
const lines = composeFrameLines(header, body, footer, rows, cols, bodyScroll);
|
|
83
86
|
const patch = diffFrame(painted?.cols === cols ? painted.lines : undefined, lines);
|
|
84
87
|
painted = { cols, lines };
|
|
@@ -86,6 +89,19 @@ export async function runApp() {
|
|
|
86
89
|
process.stdout.write(patch);
|
|
87
90
|
scheduleBusyTick(active?.isBusy?.() === true);
|
|
88
91
|
}
|
|
92
|
+
function maxScroll() {
|
|
93
|
+
return Math.max(0, lastBody.length - lastBody.height);
|
|
94
|
+
}
|
|
95
|
+
function scrollPercent() {
|
|
96
|
+
const max = maxScroll();
|
|
97
|
+
if (max === 0)
|
|
98
|
+
return undefined;
|
|
99
|
+
return `${Math.round((Math.min(scroll, max) / max) * 100)}%`;
|
|
100
|
+
}
|
|
101
|
+
function scrollTo(next) {
|
|
102
|
+
scroll = Math.min(Math.max(0, next), maxScroll());
|
|
103
|
+
render();
|
|
104
|
+
}
|
|
89
105
|
function scheduleBusyTick(busy) {
|
|
90
106
|
if (busy && running && busyTimer === undefined) {
|
|
91
107
|
busyTimer = setTimeout(() => {
|
|
@@ -139,8 +155,16 @@ export async function runApp() {
|
|
|
139
155
|
return;
|
|
140
156
|
}
|
|
141
157
|
const page = Math.max(1, ctx.bodyRows - 2);
|
|
142
|
-
|
|
143
|
-
|
|
158
|
+
scrollTo(scroll + g.scrollBy * page);
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
if ((g.scrollLines !== undefined || g.scrollTo !== undefined) && active?.scrollsBody?.()) {
|
|
162
|
+
if (g.scrollTo === 'top')
|
|
163
|
+
scrollTo(0);
|
|
164
|
+
else if (g.scrollTo === 'end')
|
|
165
|
+
scrollTo(maxScroll());
|
|
166
|
+
else
|
|
167
|
+
scrollTo(scroll + (g.scrollLines ?? 0));
|
|
144
168
|
return;
|
|
145
169
|
}
|
|
146
170
|
active?.handleKey?.(key, ctx);
|
package/dist/app/chrome.js
CHANGED
|
@@ -110,12 +110,23 @@ function interactiveFooterHint(tabCount, cols) {
|
|
|
110
110
|
];
|
|
111
111
|
return fitFooterHint(cols, ...candidates);
|
|
112
112
|
}
|
|
113
|
-
export function renderFooter(_active, cols, tabCount, overrideHint, lineCount = FOOTER_LINES) {
|
|
113
|
+
export function renderFooter(_active, cols, tabCount, overrideHint, lineCount = FOOTER_LINES, position) {
|
|
114
114
|
if (lineCount === 0)
|
|
115
115
|
return [];
|
|
116
116
|
const rule = renderRule(cols);
|
|
117
117
|
const hintText = overrideHint ?? interactiveFooterHint(tabCount, cols);
|
|
118
118
|
const indent = visualWidth(space.indent + hintText) <= cols ? space.indent : '';
|
|
119
119
|
const hint = indent + type.hint(hintText);
|
|
120
|
-
return lineCount === 1
|
|
120
|
+
return lineCount === 1
|
|
121
|
+
? [withPosition(hint, hintText, indent, position, cols)]
|
|
122
|
+
: [rule, withPosition(hint, hintText, indent, position, cols)];
|
|
123
|
+
}
|
|
124
|
+
function withPosition(hint, hintText, indent, position, cols) {
|
|
125
|
+
if (position === undefined)
|
|
126
|
+
return hint;
|
|
127
|
+
const margin = visualWidth(space.indent) < cols ? space.indent : '';
|
|
128
|
+
const gap = cols - visualWidth(indent + hintText) - visualWidth(position) - visualWidth(margin);
|
|
129
|
+
if (gap < 2)
|
|
130
|
+
return hint;
|
|
131
|
+
return hint + ' '.repeat(gap) + type.hint(position) + margin;
|
|
121
132
|
}
|
package/dist/app/keys.js
CHANGED
|
@@ -117,8 +117,16 @@ export function routeGlobalKey(key, viewIds, current) {
|
|
|
117
117
|
}
|
|
118
118
|
if (key === '\x1b[5~')
|
|
119
119
|
return { scrollBy: -1, handled: true };
|
|
120
|
-
if (key === '\x1b[6~')
|
|
120
|
+
if (key === '\x1b[6~' || key === ' ')
|
|
121
121
|
return { scrollBy: 1, handled: true };
|
|
122
|
+
if (key === '\x1b[A')
|
|
123
|
+
return { scrollLines: -1, handled: true };
|
|
124
|
+
if (key === '\x1b[B')
|
|
125
|
+
return { scrollLines: 1, handled: true };
|
|
126
|
+
if (key === '\x1b[H')
|
|
127
|
+
return { scrollTo: 'top', handled: true };
|
|
128
|
+
if (key === '\x1b[F')
|
|
129
|
+
return { scrollTo: 'end', handled: true };
|
|
122
130
|
if (/^[1-9]$/.test(key)) {
|
|
123
131
|
const idx = Number(key) - 1;
|
|
124
132
|
if (idx < viewIds.length)
|
package/dist/app/views/docs.js
CHANGED
|
@@ -4,6 +4,7 @@ import { TextField } from '../fields/text-field.js';
|
|
|
4
4
|
import { renderDocs } from './docs-render.js';
|
|
5
5
|
import { setVimKeysActive } from '../../core/vim-keys.js';
|
|
6
6
|
import { pickIcon } from '../../core/icons.js';
|
|
7
|
+
import { glyph } from '../../core/theme.js';
|
|
7
8
|
import { fmt, getCurrentLanguage, t } from '../../i18n/index.js';
|
|
8
9
|
import { sanitizeTerminalLine, truncate } from '../../core/text.js';
|
|
9
10
|
import { localizeDocSections, fetchSections, fetchDocMetadata, fetchSectionMetadata, searchDocuments, getArchivedGroups, displayDocTitle, loadDocForReader, openDocsInBrowser, docsUrlFromPath, clearDocsCache, } from '../../features/docs.js';
|
|
@@ -418,6 +419,9 @@ export const docsView = {
|
|
|
418
419
|
capturesInput() {
|
|
419
420
|
return state.mode === 'search';
|
|
420
421
|
},
|
|
422
|
+
scrollsBody() {
|
|
423
|
+
return state.mode === 'reader' && state.readerLinksField === undefined;
|
|
424
|
+
},
|
|
421
425
|
capturesPageKeys() {
|
|
422
426
|
return (state.mode === 'sections' ||
|
|
423
427
|
state.mode === 'files' ||
|
|
@@ -439,7 +443,7 @@ export const docsView = {
|
|
|
439
443
|
const dot = pickIcon('·', '-');
|
|
440
444
|
const hasLinks = (state.readerLinks?.length ?? 0) > 0;
|
|
441
445
|
const linkHint = hasLinks ? `f ${trans.docs.readerLinksHint} ${dot} ` : '';
|
|
442
|
-
const pageHint =
|
|
446
|
+
const pageHint = `${glyph.updown()} PgUp/PgDn ${dot} `;
|
|
443
447
|
const localFull = `${pageHint}${linkHint}b ${trans.docs.openBrowser} ${dot} Esc ${dot} q ${trans.menu.hintQuit}`;
|
|
444
448
|
const localCompact = `${pageHint}${hasLinks ? `f ${dot} ` : ''}b ${dot} Esc ${dot} q`;
|
|
445
449
|
return fitFooterHint(cols, `${digitTabHint(tabCount)}${localFull}`, localFull, localCompact, `${hasLinks ? 'f ' : ''}b Esc q`, 'Esc q', 'q');
|
|
@@ -4,7 +4,7 @@ import { c, type, space, glyph } from '../core/theme.js';
|
|
|
4
4
|
import { pickIcon } from '../core/icons.js';
|
|
5
5
|
import { padEndV, sanitizeTerminalLine, sanitizeTerminalText, truncate, visualWidth, wrapAnsiWithIndent, wrapAnsiToVisualWidth, } from '../core/text.js';
|
|
6
6
|
import { t } from '../i18n/index.js';
|
|
7
|
-
import { addLocalDays } from '../core/calendar-day.js';
|
|
7
|
+
import { addLocalDays, localDayDifference } from '../core/calendar-day.js';
|
|
8
8
|
import { countdownParts, isCountdownUrgent, buildExportFilename } from './calendar-query.js';
|
|
9
9
|
import { loadFeedCache, saveFeedCache } from './calendar-store.js';
|
|
10
10
|
import { writeFileSync, existsSync } from 'fs';
|
|
@@ -146,17 +146,38 @@ export function renderEventsTable(events, options) {
|
|
|
146
146
|
}
|
|
147
147
|
return lines.join('\n');
|
|
148
148
|
}
|
|
149
|
+
export function eventProximity(startDate, now) {
|
|
150
|
+
const days = localDayDifference(now, startDate);
|
|
151
|
+
if (days <= 0)
|
|
152
|
+
return 'today';
|
|
153
|
+
if (days === 1)
|
|
154
|
+
return 'tomorrow';
|
|
155
|
+
if (days <= 7)
|
|
156
|
+
return 'week';
|
|
157
|
+
return 'later';
|
|
158
|
+
}
|
|
159
|
+
const SOURCE_TAG = /^(\[[^\]]{1,16}\])(\s+)(?=\S)/;
|
|
160
|
+
function styleTitle(title, style) {
|
|
161
|
+
const match = SOURCE_TAG.exec(title);
|
|
162
|
+
if (!match?.[1] || !match[2])
|
|
163
|
+
return style(title);
|
|
164
|
+
return `${type.hint(match[1])}${match[2]}${style(title.slice(match[0].length))}`;
|
|
165
|
+
}
|
|
149
166
|
export function renderEventBrief(e, now) {
|
|
150
167
|
const dot = pickIcon('·', '-');
|
|
151
|
-
const
|
|
152
|
-
|
|
153
|
-
|
|
168
|
+
const proximity = eventProximity(e.startDate, now);
|
|
169
|
+
const dateStyle = proximity === 'today'
|
|
170
|
+
? type.active
|
|
171
|
+
: proximity === 'tomorrow'
|
|
172
|
+
? c.brand
|
|
173
|
+
: proximity === 'week'
|
|
174
|
+
? type.body
|
|
175
|
+
: type.hint;
|
|
154
176
|
const dateTime = `${e.date}${e.time ? ' ' + e.time : ''}`;
|
|
155
|
-
const marker =
|
|
156
|
-
const
|
|
157
|
-
const
|
|
158
|
-
|
|
159
|
-
return `${space.indent}${marker} ${dateStyled} ${dot} ${titleStyled}${recurringMark}`;
|
|
177
|
+
const marker = proximity === 'today' ? type.active(pickIcon('●', '*')) : dateStyle(pickIcon('·', '-'));
|
|
178
|
+
const titleStyled = styleTitle(e.title, proximity === 'today' ? type.active : type.body);
|
|
179
|
+
const recurringMark = e.recurring ? ` ${type.hint(pickIcon('↻', '~'))}` : '';
|
|
180
|
+
return `${space.indent}${marker} ${dateStyle(dateTime)} ${type.hint(dot)} ${titleStyled}${recurringMark}`;
|
|
160
181
|
}
|
|
161
182
|
export function renderCountdownBanner(event, now, cols = Number.POSITIVE_INFINITY) {
|
|
162
183
|
if (!event)
|