papilot 0.8.2 → 0.8.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.
Files changed (115) hide show
  1. package/README.md +2 -2
  2. package/bin/doctor.mjs +1 -1
  3. package/dist/assets/{_virtual_papilot-licenses-CMfCtEYQ.js → _virtual_papilot-licenses-YJbh9c1G.js} +1 -1
  4. package/dist/assets/index-0siHHtfz.js +267 -0
  5. package/dist/assets/index-CxdYm81a.css +1 -0
  6. package/dist/assets/{math-CEU4m4-H.js → math-ibBpTslk.js} +1 -1
  7. package/dist/assets/{pdf-view-HWRGBLsZ.js → pdf-view-CD1kNKYH.js} +1 -1
  8. package/dist/index.html +2 -2
  9. package/engine/dist/web/assets/FullPaper-DUgLb01O.js +54 -0
  10. package/engine/dist/web/assets/index-D3_ZFdT8.css +1 -0
  11. package/engine/dist/web/assets/index-DJywe0wU.js +19 -0
  12. package/engine/dist/web/assets/pdf.worker.min-CjEcRF4W.mjs +28 -0
  13. package/{vendor/latex-review → engine}/dist/web/index.html +2 -2
  14. package/engine/package-lock.json +3390 -0
  15. package/engine/package.json +50 -0
  16. package/{vendor/latex-review → engine}/src/core/build.ts +22 -6
  17. package/engine/src/core/progress.ts +67 -0
  18. package/{vendor/latex-review → engine}/src/core/review.ts +32 -0
  19. package/{vendor/latex-review → engine}/src/render/inline.ts +1 -0
  20. package/{vendor/latex-review → engine}/src/render/locate.ts +26 -16
  21. package/engine/src/render/positions.ts +34 -0
  22. package/engine/src/render/synctex.ts +188 -0
  23. package/engine/src/render/synctex.wasm +0 -0
  24. package/engine/src/render/synctex.wasm.LICENSE +50 -0
  25. package/{vendor/latex-review → engine}/src/server.ts +5 -3
  26. package/{vendor/latex-review → engine}/src/types.ts +13 -0
  27. package/engine/src/web/FullPaper.tsx +318 -0
  28. package/{vendor/latex-review → engine}/src/web/locales/de.json +3 -0
  29. package/{vendor/latex-review → engine}/src/web/locales/es.json +3 -0
  30. package/{vendor/latex-review → engine}/src/web/locales/fr.json +3 -0
  31. package/{vendor/latex-review → engine}/src/web/locales/it.json +3 -0
  32. package/{vendor/latex-review → engine}/src/web/locales/ja.json +3 -0
  33. package/{vendor/latex-review → engine}/src/web/locales/ko.json +3 -0
  34. package/{vendor/latex-review → engine}/src/web/locales/pt.json +3 -0
  35. package/{vendor/latex-review → engine}/src/web/locales/sv.json +3 -0
  36. package/{vendor/latex-review → engine}/src/web/locales/zh-Hant.json +3 -0
  37. package/engine/src/web/paper.ts +13 -0
  38. package/{vendor/latex-review → engine}/src/web/review.tsx +98 -1
  39. package/{vendor/latex-review → engine}/src/web/style.css +70 -0
  40. package/engine/src/web/view-preferences.ts +13 -0
  41. package/{vendor/latex-review → engine}/src/web/zh-CN.ts +3 -0
  42. package/package.json +10 -8
  43. package/src/conversation.ts +1 -1
  44. package/src/server/app.ts +16 -0
  45. package/src/server/changes.ts +12 -3
  46. package/src/server/imports.ts +9 -4
  47. package/src/server/main.ts +3 -2
  48. package/src/server/manuscript.ts +5 -4
  49. package/src/server/paths.ts +1 -1
  50. package/src/server/start.ts +3 -10
  51. package/src/server/update-install.ts +8 -25
  52. package/src/server/updates.ts +43 -25
  53. package/src/shared.ts +6 -3
  54. package/vendor/latex-review/package-lock.json +4 -3106
  55. package/vendor/latex-review/package.json +3 -46
  56. package/README.de.md +0 -110
  57. package/README.es.md +0 -110
  58. package/README.fr.md +0 -110
  59. package/README.it.md +0 -110
  60. package/README.ja.md +0 -110
  61. package/README.ko.md +0 -110
  62. package/README.pt.md +0 -110
  63. package/README.sv.md +0 -110
  64. package/README.zh-CN.md +0 -110
  65. package/README.zh-Hant.md +0 -110
  66. package/dist/assets/index-oAIkxhfp.js +0 -265
  67. package/dist/assets/index-t_qCOF_B.css +0 -1
  68. package/vendor/latex-review/dist/web/assets/index-DVs7cTBl.js +0 -19
  69. package/vendor/latex-review/dist/web/assets/index-ysOpu05D.css +0 -1
  70. /package/{vendor/latex-review → engine}/LICENSE +0 -0
  71. /package/{vendor/latex-review → engine}/dist/web/assets/TeXGyrePagellaX-Bold-CEtYdOXj.otf +0 -0
  72. /package/{vendor/latex-review → engine}/dist/web/assets/TeXGyrePagellaX-Regular-DQJjZE_L.otf +0 -0
  73. /package/{vendor/latex-review → engine}/src/cli.ts +0 -0
  74. /package/{vendor/latex-review → engine}/src/client.ts +0 -0
  75. /package/{vendor/latex-review → engine}/src/core/bib.ts +0 -0
  76. /package/{vendor/latex-review → engine}/src/core/changes.ts +0 -0
  77. /package/{vendor/latex-review → engine}/src/core/config.ts +0 -0
  78. /package/{vendor/latex-review → engine}/src/core/current-commit.ts +0 -0
  79. /package/{vendor/latex-review → engine}/src/core/delivery.ts +0 -0
  80. /package/{vendor/latex-review → engine}/src/core/diff.ts +0 -0
  81. /package/{vendor/latex-review → engine}/src/core/files.ts +0 -0
  82. /package/{vendor/latex-review → engine}/src/core/git.ts +0 -0
  83. /package/{vendor/latex-review → engine}/src/core/outline.ts +0 -0
  84. /package/{vendor/latex-review → engine}/src/core/process.ts +0 -0
  85. /package/{vendor/latex-review → engine}/src/core/publish.ts +0 -0
  86. /package/{vendor/latex-review → engine}/src/core/reconcile.ts +0 -0
  87. /package/{vendor/latex-review → engine}/src/core/store.ts +0 -0
  88. /package/{vendor/latex-review → engine}/src/core/tasks.ts +0 -0
  89. /package/{vendor/latex-review → engine}/src/core/verify.ts +0 -0
  90. /package/{vendor/latex-review → engine}/src/core/view.ts +0 -0
  91. /package/{vendor/latex-review → engine}/src/locales.ts +0 -0
  92. /package/{vendor/latex-review → engine}/src/render/effects.ts +0 -0
  93. /package/{vendor/latex-review → engine}/src/render/pdf.ts +0 -0
  94. /package/{vendor/latex-review → engine}/src/render/pdfdiff.ts +0 -0
  95. /package/{vendor/latex-review → engine}/src/render/round.ts +0 -0
  96. /package/{vendor/latex-review → engine}/src/render/worker.ts +0 -0
  97. /package/{vendor/latex-review → engine}/src/runtime.ts +0 -0
  98. /package/{vendor/latex-review → engine}/src/web/ChangeMap.tsx +0 -0
  99. /package/{vendor/latex-review → engine}/src/web/PdfStrip.tsx +0 -0
  100. /package/{vendor/latex-review → engine}/src/web/effect-preferences.ts +0 -0
  101. /package/{vendor/latex-review → engine}/src/web/fonts/GUST-FONT-LICENSE.txt +0 -0
  102. /package/{vendor/latex-review → engine}/src/web/fonts/LPPL.txt +0 -0
  103. /package/{vendor/latex-review → engine}/src/web/fonts/MANIFEST-newpx.txt +0 -0
  104. /package/{vendor/latex-review → engine}/src/web/fonts/README-newpx.txt +0 -0
  105. /package/{vendor/latex-review → engine}/src/web/fonts/README.md +0 -0
  106. /package/{vendor/latex-review → engine}/src/web/fonts/TeXGyrePagellaX-Bold.otf +0 -0
  107. /package/{vendor/latex-review → engine}/src/web/fonts/TeXGyrePagellaX-Regular.otf +0 -0
  108. /package/{vendor/latex-review → engine}/src/web/i18n.ts +0 -0
  109. /package/{vendor/latex-review → engine}/src/web/index.html +0 -0
  110. /package/{vendor/latex-review → engine}/src/web/locales/index.ts +0 -0
  111. /package/{vendor/latex-review → engine}/src/web/main.tsx +0 -0
  112. /package/{vendor/latex-review → engine}/src/web/model.ts +0 -0
  113. /package/{vendor/latex-review → engine}/src/web/overview.tsx +0 -0
  114. /package/{vendor/latex-review → engine}/src/web/references.tsx +0 -0
  115. /package/{vendor/latex-review → engine}/src/web/shortcuts.ts +0 -0
@@ -3,16 +3,16 @@ import path from 'node:path';
3
3
  import { spawn } from 'node:child_process';
4
4
  import { randomUUID } from 'node:crypto';
5
5
  import { devRevision, newerVersion, readJson, stableVersion, writeJson } from '../../bin/installation.mjs';
6
- import { desktopSupports, installRelease, loaderFor, NEEDS_INSTALLER, npmCli } from './update-install.ts';
6
+ import { installRelease, loaderFor, npmCli } from './update-install.ts';
7
7
  import type { UpdateChannel, UpdateStatus } from '../shared.ts';
8
8
 
9
9
  const activePhases = new Set(['installing', 'waiting', 'restarting']);
10
10
  export const alive = (pid: number) => { try { process.kill(pid, 0); return true; } catch { return false; } };
11
- async function releaseInfo(url: string, fetcher: typeof fetch) {
11
+ async function releaseInfo(url: string, fetcher: typeof fetch, limit = 256000) {
12
12
  const response = await fetcher(url, { signal: AbortSignal.timeout(10000), redirect: 'error', headers: { Accept: 'application/json', 'User-Agent': 'Papilot' } });
13
13
  if (!response.ok) throw new Error('Could not check for updates.');
14
14
  const text = await response.text();
15
- if (text.length > 256000) throw new Error('Invalid release information.');
15
+ if (text.length > limit) throw new Error('Invalid release information.');
16
16
  return JSON.parse(text);
17
17
  }
18
18
  // The package.json of the newest release on a channel.
@@ -30,14 +30,29 @@ export async function latestManifest(channel: UpdateChannel = 'stable', fetcher
30
30
  return pkg;
31
31
  }
32
32
  export const latestRelease = async (channel: UpdateChannel = 'stable', fetcher = fetch) => (await latestManifest(channel, fetcher)).version as string;
33
+ export const DESKTOP_RELEASES = 'https://github.com/PapilotAI/papilot/releases';
34
+ // npm can be published before desktop builds finish. Only advertise releases
35
+ // with an uploaded installer for this OS and CPU, and link to that exact release.
36
+ export async function latestDesktopRelease(platform = process.platform, arch = process.arch, fetcher = fetch) {
37
+ const releases = await releaseInfo('https://api.github.com/repos/PapilotAI/papilot/releases?per_page=100', fetcher, 4 * 1024 * 1024);
38
+ if (!Array.isArray(releases)) throw new Error('Invalid release information.');
39
+ let latest: { version: string; downloadUrl: string } | null = null;
40
+ for (const release of releases) {
41
+ const version = typeof release.tag_name === 'string' ? release.tag_name.replace(/^v/, '') : '';
42
+ if (release.draft !== false || release.prerelease !== false || !stableVersion(version) || !Array.isArray(release.assets)) continue;
43
+ const names = platform === 'darwin' && ['arm64', 'x64'].includes(arch) ? [`Papilot-${version}-mac-${arch}.dmg`, `Papilot-${version}-mac-${arch}.zip`]
44
+ : platform === 'linux' && ['arm64', 'x64'].includes(arch) ? [`Papilot-${version}-linux-${arch === 'x64' ? 'x86_64' : arch}.AppImage`, `Papilot-${version}-linux-${arch === 'x64' ? 'amd64' : arch}.deb`] : [];
45
+ if (!release.assets.some((asset: any) => asset.state === 'uploaded' && asset.size > 0 && names.includes(asset.name))) continue;
46
+ if (!latest || newerVersion(version, latest.version)) latest = { version, downloadUrl: `${DESKTOP_RELEASES}/tag/${release.tag_name}` };
47
+ }
48
+ return latest;
49
+ }
33
50
  const validTarget = (version: unknown, channel: UpdateChannel): version is string => channel === 'dev' ? Boolean(devRevision(version)) : stableVersion(version);
34
51
  const available = (next: string, current: string, channel: UpdateChannel) => validTarget(next, channel)
35
52
  && (channel === 'dev' || devRevision(current) ? next !== current : newerVersion(next, current));
36
53
  interface Options {
37
- // Desktop builds that cannot install releases link to this download page.
38
- downloadUrl?: string;
39
- // A desktop installer that runs newer releases from npm (see desktop/runtime.ts).
40
- desktop?: { shell: number; installer: string; downloadUrl: string };
54
+ desktop?: boolean;
55
+ latestDesktop?: () => Promise<{ version: string; downloadUrl: string } | null>;
41
56
  root: string; data: string; version: string; port: number;
42
57
  busy: () => boolean; shutdown: () => void;
43
58
  latest?: (channel: UpdateChannel) => Promise<string | Record<string, any>>; install?: (data: string, version: string) => Promise<string>;
@@ -55,32 +70,28 @@ export class Updates {
55
70
  private file: string;
56
71
  private lock: string;
57
72
  private channel: UpdateChannel;
58
- // The latest release needs a newer desktop installer.
59
- private needsInstaller = false;
73
+ private downloadUrl = DESKTOP_RELEASES;
60
74
  constructor(private options: Options) {
61
- this.file = path.join(options.data, 'updates/status.json'); this.lock = path.join(options.data, 'updates/install.lock');
75
+ this.file = path.join(options.data, options.desktop ? 'updates/desktop/status.json' : 'updates/status.json'); this.lock = path.join(options.data, 'updates/install.lock');
62
76
  const preference = readJson(path.join(options.data, 'updates/channel.json'))?.channel;
63
- this.channel = options.downloadUrl || options.desktop ? 'stable' : preference === 'dev' || preference === 'stable' ? preference : devRevision(options.version) ? 'dev' : 'stable';
64
- let supported = options.supported ?? true;
65
- if (options.supported === undefined) try { npmCli(); } catch { supported = false; }
77
+ this.channel = options.desktop ? 'stable' : preference === 'dev' || preference === 'stable' ? preference : devRevision(options.version) ? 'dev' : 'stable';
78
+ let supported = options.desktop ? false : options.supported ?? true;
79
+ if (!options.desktop && options.supported === undefined) try { npmCli(); } catch { supported = false; }
66
80
  this.state = { currentVersion: options.version, latestVersion: null, phase: 'idle', checkedAt: null, error: null, canUpdate: supported };
67
- const saved = readJson(this.file);
81
+ const saved = options.desktop ? null : readJson(this.file);
68
82
  if (saved && ['failed', 'updated', 'installing', 'waiting', 'restarting'].includes(saved.phase)) this.state = { ...this.state, ...saved, currentVersion: options.version, canUpdate: supported };
69
83
  if (saved && activePhases.has(saved.phase) && !alive(saved.pid)) this.change({ phase: 'failed', checkedAt: Date.now(), error: 'The update was interrupted. Your current installation is still available; retry the update.' });
70
84
  }
71
85
  snapshot(): UpdateStatus {
72
- if (this.options.downloadUrl) return { currentVersion: this.options.version, latestVersion: null, phase: 'idle', checkedAt: null, error: null, canUpdate: false, channel: 'stable', downloadUrl: this.options.downloadUrl };
73
86
  const saved = readJson(this.file);
74
87
  if (saved && activePhases.has(this.state.phase) && ['updated', 'failed'].includes(saved.phase)) this.state = { ...this.state, ...saved, currentVersion: this.options.version };
75
88
  const { currentVersion, latestVersion, phase, checkedAt, error, canUpdate } = this.state, desktop = this.options.desktop;
76
- if (desktop) return { currentVersion, latestVersion, phase, checkedAt, error, channel: this.channel, installerVersion: desktop.installer,
77
- ...(this.needsInstaller ? { canUpdate: false, downloadUrl: desktop.downloadUrl } : { canUpdate }) };
89
+ if (desktop) return { currentVersion, latestVersion, phase, checkedAt, error, channel: 'stable', canUpdate: false, downloadUrl: this.downloadUrl };
78
90
  return { currentVersion, latestVersion, phase, checkedAt, error, canUpdate, channel: this.channel };
79
91
  }
80
92
  get restarting() { return this.snapshot().phase === 'restarting'; }
81
93
  enter() { this.inFlight++; let released = false; return () => { if (!released) { released = true; this.inFlight--; } }; }
82
94
  start() {
83
- if (this.options.downloadUrl) return;
84
95
  if (this.state.phase !== 'failed') void this.check();
85
96
  this.timer = setInterval(() => {
86
97
  if (this.state.phase === 'waiting') void this.activate();
@@ -93,7 +104,7 @@ export class Updates {
93
104
  writeJson(this.file, { ...this.state, pid: process.pid });
94
105
  }
95
106
  setChannel(channel: unknown) {
96
- if (this.options.downloadUrl || this.options.desktop) throw new Error('Download desktop updates from GitHub Releases.');
107
+ if (this.options.desktop) throw new Error('Download desktop updates from GitHub Releases.');
97
108
  if (channel !== 'stable' && channel !== 'dev') throw new Error('Choose a valid update channel.');
98
109
  if (activePhases.has(this.state.phase)) throw new Error('Wait for the current update to finish before changing channels.');
99
110
  if (channel === this.channel) return this.check();
@@ -104,17 +115,25 @@ export class Updates {
104
115
  return this.check();
105
116
  }
106
117
  check(): Promise<UpdateStatus> {
107
- if (this.options.downloadUrl) return Promise.resolve(this.snapshot());
108
118
  if (this.checking) return this.checking;
109
119
  if (activePhases.has(this.state.phase)) return Promise.resolve(this.snapshot());
110
120
  this.change({ phase: 'checking' });
111
121
  const channel = this.channel;
112
- const checking = (this.options.latest || latestManifest)(channel).then(found => {
113
- const manifest = typeof found === 'string' ? { version: found } : found, version = manifest?.version;
122
+ const discovery = this.options.desktop ? (this.options.latestDesktop || latestDesktopRelease)() : (this.options.latest || latestManifest)(channel);
123
+ const checking = discovery.then(found => {
124
+ const manifest = typeof found === 'string' ? { version: found } : found;
125
+ if (!manifest && this.options.desktop) {
126
+ if (!this.closed && this.checking === checking) {
127
+ this.downloadUrl = DESKTOP_RELEASES;
128
+ this.change({ latestVersion: null, checkedAt: Date.now(), error: null, phase: 'idle' });
129
+ }
130
+ return this.snapshot();
131
+ }
132
+ const version = manifest?.version;
114
133
  if (!validTarget(version, channel)) throw new Error('Invalid release version.');
115
134
  if (!this.closed && this.checking === checking && !activePhases.has(this.state.phase)) {
116
135
  const newer = available(version, this.options.version, channel);
117
- this.needsInstaller = Boolean(this.options.desktop && newer && !desktopSupports(manifest, this.options.desktop.shell));
136
+ if (this.options.desktop) this.downloadUrl = manifest!.downloadUrl;
118
137
  this.change({ latestVersion: version, checkedAt: Date.now(), error: null, phase: newer ? 'available' : 'idle' });
119
138
  }
120
139
  return this.snapshot();
@@ -126,9 +145,8 @@ export class Updates {
126
145
  return this.checking;
127
146
  }
128
147
  install(version: unknown) {
129
- if (this.options.downloadUrl) throw new Error('Download desktop updates from GitHub Releases.');
148
+ if (this.options.desktop) throw new Error('Download desktop updates from GitHub Releases.');
130
149
  if (activePhases.has(this.state.phase)) return this.snapshot();
131
- if (this.needsInstaller) throw new Error(NEEDS_INSTALLER);
132
150
  if (!this.state.canUpdate) throw new Error('npm is unavailable to Papilot.');
133
151
  if (!validTarget(version, this.channel) || version !== this.state.latestVersion || !available(version, this.options.version, this.channel)) throw new Error('Check for a newer release before updating.');
134
152
  fs.mkdirSync(path.dirname(this.lock), { recursive: true, mode: 0o700 });
package/src/shared.ts CHANGED
@@ -11,8 +11,6 @@ export type UpdateChannel = 'stable' | 'dev';
11
11
  export interface UpdateStatus {
12
12
  // Present when the update must be downloaded as a new desktop app.
13
13
  downloadUrl?: string;
14
- // The desktop app's own Papilot version; newer releases run on top of it.
15
- installerVersion?: string;
16
14
  channel?: UpdateChannel;
17
15
  currentVersion: string; latestVersion: string | null;
18
16
  phase: 'idle' | 'checking' | 'available' | 'installing' | 'waiting' | 'restarting' | 'updated' | 'failed';
@@ -126,7 +124,10 @@ export interface GitWorktree {
126
124
  }
127
125
  export interface GitGraphCommit { hash: string; parents: string[]; subject: string; author: string; date: number; refs: string[] }
128
126
  export interface GitOverview { worktrees: GitWorktree[]; commits: GitGraphCommit[]; truncated: boolean }
129
- export interface ManuscriptBuild { status: 'building' | 'ready' | 'failed'; key?: string; pages?: [number, number][]; builtAt?: number; error?: string; current: boolean; problems?: BuildProblemCounts }
127
+ // A running compilation, from latexmk's output: its step, the TeX pass, and
128
+ // the pages typeset in that pass against the last known page count.
129
+ export interface BuildProgress { step: 'tex' | 'bibliography' | 'pdf'; pass: number; pages: number; total: number | null; startedAt: number }
130
+ export interface ManuscriptBuild { status: 'building' | 'ready' | 'failed'; key?: string; pages?: [number, number][]; builtAt?: number; error?: string; current: boolean; problems?: BuildProblemCounts; progress?: BuildProgress }
130
131
  // What the last finished build reported. Errors stop the build; warnings are
131
132
  // mistakes in the PDF (?? and missing citations); minor problems are
132
133
  // typesetting (overfull lines) and package remarks, listed but never counted.
@@ -156,6 +157,8 @@ export interface PaperImport {
156
157
  id: string; path: string; name: string; copied: boolean; candidates: string[]; main: string; engine: string;
157
158
  phase: 'choose' | 'building' | 'failed' | 'ready' | 'repairing';
158
159
  error?: string; summary?: string; versioned?: boolean;
160
+ // While building: when it started, and the compilation's progress.
161
+ startedAt?: number; progress?: BuildProgress;
159
162
  }
160
163
  // The paper's bibliography as the References panel reads it: the review
161
164
  // engine's report of every entry, with online checks and the entries the