@nbtca/prompt 1.5.1 → 1.5.2

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 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
- scroll = Math.max(0, scroll + g.scrollBy * page);
143
- render();
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);
@@ -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 ? [hint] : [rule, hint];
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)
@@ -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 = `PgUp/PgDn ${dot} `;
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');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nbtca/prompt",
3
- "version": "1.5.1",
3
+ "version": "1.5.2",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "exports": {