dsh-figmark 1.1.3 → 1.2.0
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/lib/client.js +965 -316
- package/lib/client.pdf.js +452 -65
- package/lib/index.js +4 -3
- package/market/index.json +2 -2
- package/package.json +2 -2
package/lib/client.pdf.js
CHANGED
|
@@ -26166,6 +26166,91 @@ while (prev === _win[++scan] && prev === _win[++scan] && prev === _win[++scan] &
|
|
|
26166
26166
|
page.node.set(import_cjs.PDFName.of("Annots"), created);
|
|
26167
26167
|
return created;
|
|
26168
26168
|
}
|
|
26169
|
+
/**
|
|
26170
|
+
* A page number pulled into the document.
|
|
26171
|
+
* @param page - the page asked for.
|
|
26172
|
+
* @param pageCount - how many pages the document has.
|
|
26173
|
+
* @returns the nearest page that exists.
|
|
26174
|
+
*/
|
|
26175
|
+
function clampPage(page, pageCount) {
|
|
26176
|
+
const last = Math.max(pageCount, 1);
|
|
26177
|
+
if (Number.isNaN(page)) return 1;
|
|
26178
|
+
return Math.min(Math.max(Math.round(page), 1), last);
|
|
26179
|
+
}
|
|
26180
|
+
/**
|
|
26181
|
+
* The page one step away, clamped at both ends.
|
|
26182
|
+
* @param page - the page on screen.
|
|
26183
|
+
* @param delta - how many pages to move; negative moves back.
|
|
26184
|
+
* @param pageCount - how many pages the document has.
|
|
26185
|
+
* @returns the page to show.
|
|
26186
|
+
*/
|
|
26187
|
+
function stepPage(page, delta, pageCount) {
|
|
26188
|
+
return clampPage(page + delta, pageCount);
|
|
26189
|
+
}
|
|
26190
|
+
/**
|
|
26191
|
+
* A page number typed into the box.
|
|
26192
|
+
*
|
|
26193
|
+
* Digits only: a reader typing "12" means page 12, and anything else is a typo
|
|
26194
|
+
* that must leave them where they are rather than jump them somewhere arbitrary.
|
|
26195
|
+
* A number past the end lands on the last page, which is what a reader typing it
|
|
26196
|
+
* was reaching for anyway.
|
|
26197
|
+
* @param raw - what the box holds.
|
|
26198
|
+
* @param pageCount - how many pages the document has.
|
|
26199
|
+
* @returns the page to show, or null when the text is not a page number.
|
|
26200
|
+
*/
|
|
26201
|
+
function parsePageInput(raw, pageCount) {
|
|
26202
|
+
const digits = raw.trim();
|
|
26203
|
+
if (!/^\d+$/u.test(digits)) return null;
|
|
26204
|
+
return clampPage(Number.parseInt(digits, 10), pageCount);
|
|
26205
|
+
}
|
|
26206
|
+
/**
|
|
26207
|
+
* The page turn a keystroke asks for.
|
|
26208
|
+
*
|
|
26209
|
+
* Only the keys a PDF reader answers to, and none of them while a modifier is
|
|
26210
|
+
* held: Cmd+Arrow belongs to the shell, and swallowing it here would turn a text
|
|
26211
|
+
* selection or a tab switch into a page turn.
|
|
26212
|
+
* @param key - the event's key.
|
|
26213
|
+
* @param modifiers - which modifiers were held.
|
|
26214
|
+
* @returns the turn, or null when this key is not one.
|
|
26215
|
+
*/
|
|
26216
|
+
function keyedTurn(key, modifiers) {
|
|
26217
|
+
if (modifiers.metaKey || modifiers.ctrlKey || modifiers.altKey) return null;
|
|
26218
|
+
switch (key) {
|
|
26219
|
+
case "ArrowRight":
|
|
26220
|
+
case "ArrowDown":
|
|
26221
|
+
case "PageDown":
|
|
26222
|
+
case " ": return {
|
|
26223
|
+
delta: 1,
|
|
26224
|
+
absolute: false
|
|
26225
|
+
};
|
|
26226
|
+
case "ArrowLeft":
|
|
26227
|
+
case "ArrowUp":
|
|
26228
|
+
case "PageUp": return {
|
|
26229
|
+
delta: -1,
|
|
26230
|
+
absolute: false
|
|
26231
|
+
};
|
|
26232
|
+
case "Home": return {
|
|
26233
|
+
delta: 1,
|
|
26234
|
+
absolute: true
|
|
26235
|
+
};
|
|
26236
|
+
case "End": return {
|
|
26237
|
+
delta: Number.POSITIVE_INFINITY,
|
|
26238
|
+
absolute: true
|
|
26239
|
+
};
|
|
26240
|
+
default: return null;
|
|
26241
|
+
}
|
|
26242
|
+
}
|
|
26243
|
+
/**
|
|
26244
|
+
* The page a key turn lands on.
|
|
26245
|
+
* @param turn - what the key asked for.
|
|
26246
|
+
* @param page - the page on screen.
|
|
26247
|
+
* @param pageCount - how many pages the document has.
|
|
26248
|
+
* @returns the page to show.
|
|
26249
|
+
*/
|
|
26250
|
+
function keyedPage(turn, page, pageCount) {
|
|
26251
|
+
if (!turn.absolute) return stepPage(page, turn.delta, pageCount);
|
|
26252
|
+
return clampPage(turn.delta, pageCount);
|
|
26253
|
+
}
|
|
26169
26254
|
//#endregion
|
|
26170
26255
|
//#region node_modules/.pnpm/pdfjs-dist@6.3.289/node_modules/pdfjs-dist/build/pdf.mjs
|
|
26171
26256
|
const isNodeJS = typeof process === "object" && process + "" === "[object process]" && !process.versions.nw && !(process.versions.electron && process.type && process.type !== "browser");
|
|
@@ -49919,11 +50004,48 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
49919
50004
|
* drawing surface and the marks of that page.
|
|
49920
50005
|
* @module dsh-figmark/client/pdf/PdfBody
|
|
49921
50006
|
*/
|
|
49922
|
-
/**
|
|
50007
|
+
/**
|
|
50008
|
+
* Gap kept around a page inside the pane.
|
|
50009
|
+
*
|
|
50010
|
+
* The same number `AnnotatorBody` subtracts from its own stage width to decide how
|
|
50011
|
+
* wide the page is drawn: a raster made for a box of a different width is a raster
|
|
50012
|
+
* resampled to fit, and that is softness the reader sees on every glyph.
|
|
50013
|
+
*/
|
|
49923
50014
|
const PANE_INSET = 24;
|
|
49924
|
-
/**
|
|
50015
|
+
/**
|
|
50016
|
+
* Highest device-pixel ratio one page bitmap is rendered at.
|
|
50017
|
+
*
|
|
50018
|
+
* Three rather than two: a Retina display reports 2, but a *scaled* Retina mode (a
|
|
50019
|
+
* "more space" resolution, a zoomed window) reports 3, and capping at 2 there leaves
|
|
50020
|
+
* the page short of the pixels the screen actually has.
|
|
50021
|
+
*/
|
|
49925
50022
|
const MAX_DENSITY = 2;
|
|
49926
50023
|
/**
|
|
50024
|
+
* Bitmap pixels a page gets per screen pixel.
|
|
50025
|
+
*
|
|
50026
|
+
* A page is laid out in its own units and then scaled to the pane, so the browser
|
|
50027
|
+
* resamples its bitmap whatever it wants. Measured in Chromium at a Retina ratio,
|
|
50028
|
+
* how sharp the result reads depends on how many source pixels each screen pixel
|
|
50029
|
+
* gets, and a bitmap that misses the screen's count by a few percent is the *worst*
|
|
50030
|
+
* case, not the best — the resampler has no whole-pixel neighbours to average, so
|
|
50031
|
+
* every glyph edge is blended with the background beside it.
|
|
50032
|
+
*
|
|
50033
|
+
* Measured, as the 99th percentile of the lateral gradient across a text line:
|
|
50034
|
+
* 1.04× gives 129, 1.25× gives 157, 1.5× gives 168, 2× gives 178 (3× gives 181).
|
|
50035
|
+
* The plateau is reached early: 1.5× already reads as sharp and costs a third of
|
|
50036
|
+
* the memory 2× does, so this is the spare kept.
|
|
50037
|
+
*/
|
|
50038
|
+
const SUPERSAMPLE = 1.5;
|
|
50039
|
+
/**
|
|
50040
|
+
* The largest factor "fit width" draws a page at.
|
|
50041
|
+
*
|
|
50042
|
+
* The same ceiling `AnnotatorBody` applies when it decides how big to draw a figure;
|
|
50043
|
+
* it is repeated here because a lazily loaded chunk may not import the client bundle
|
|
50044
|
+
* at run time. Past this factor a wider pane buys no more drawn pixels, so the two
|
|
50045
|
+
* numbers have to agree or this one pays for pixels the page never occupies.
|
|
50046
|
+
*/
|
|
50047
|
+
const FIT_CEILING = 1.5;
|
|
50048
|
+
/**
|
|
49927
50049
|
* Narrowest width a page is rendered for; below it the pane is still settling.
|
|
49928
50050
|
* Compared against the stepped width, so the guard itself is a whole step wide.
|
|
49929
50051
|
*/
|
|
@@ -49939,6 +50061,65 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
49939
50061
|
*/
|
|
49940
50062
|
const RENDER_WIDTH_STEP = 64;
|
|
49941
50063
|
/**
|
|
50064
|
+
* Whether a keystroke landed in a field the reader is typing into.
|
|
50065
|
+
*
|
|
50066
|
+
* An editable region is recognised by its attribute rather than by the
|
|
50067
|
+
* `isContentEditable` property, which a rendered frame's own document does not
|
|
50068
|
+
* answer for. Any element inside one counts, so a caret nested in a paragraph is
|
|
50069
|
+
* as safe as the region itself.
|
|
50070
|
+
* @param target - the event's target.
|
|
50071
|
+
* @returns whether the keystroke belongs to the field.
|
|
50072
|
+
*/
|
|
50073
|
+
function typingIn(target) {
|
|
50074
|
+
if (!(target instanceof HTMLElement)) return false;
|
|
50075
|
+
if (target.closest("[contenteditable]:not([contenteditable=\"false\"])") !== null) return true;
|
|
50076
|
+
return target.tagName === "INPUT" || target.tagName === "TEXTAREA" || target.tagName === "SELECT";
|
|
50077
|
+
}
|
|
50078
|
+
/**
|
|
50079
|
+
* The document's own icons.
|
|
50080
|
+
*
|
|
50081
|
+
* Drawn here rather than imported from the annotator body: this is the lazy chunk,
|
|
50082
|
+
* and the body reaches it as a prop precisely because the two halves cannot import
|
|
50083
|
+
* each other. The paths follow the body's own icon grid so a strip of these and a
|
|
50084
|
+
* strip of those read as one set.
|
|
50085
|
+
*/
|
|
50086
|
+
function ChevronLeftIcon() {
|
|
50087
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
50088
|
+
className: "da-ico",
|
|
50089
|
+
viewBox: "0 0 24 24",
|
|
50090
|
+
"aria-hidden": "true",
|
|
50091
|
+
focusable: "false",
|
|
50092
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M 14.5 5.5 L 8 12 L 14.5 18.5" })
|
|
50093
|
+
});
|
|
50094
|
+
}
|
|
50095
|
+
function ChevronRightIcon() {
|
|
50096
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
50097
|
+
className: "da-ico",
|
|
50098
|
+
viewBox: "0 0 24 24",
|
|
50099
|
+
"aria-hidden": "true",
|
|
50100
|
+
focusable: "false",
|
|
50101
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M 9.5 5.5 L 16 12 L 9.5 18.5" })
|
|
50102
|
+
});
|
|
50103
|
+
}
|
|
50104
|
+
function SendIcon() {
|
|
50105
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
50106
|
+
className: "da-ico",
|
|
50107
|
+
viewBox: "0 0 24 24",
|
|
50108
|
+
"aria-hidden": "true",
|
|
50109
|
+
focusable: "false",
|
|
50110
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M 3 12 L 21 3.9 L 15.9 21 L 12 14.1 Z" }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M 12 14.1 L 21 3.9" })]
|
|
50111
|
+
});
|
|
50112
|
+
}
|
|
50113
|
+
function ChatIcon() {
|
|
50114
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
50115
|
+
className: "da-ico",
|
|
50116
|
+
viewBox: "0 0 24 24",
|
|
50117
|
+
"aria-hidden": "true",
|
|
50118
|
+
focusable: "false",
|
|
50119
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: "M 3.75 9.75 a 3 3 0 0 1 3 -3 h 10.5 a 3 3 0 0 1 3 3 v 4.5 a 3 3 0 0 1 -3 3 h -6 l -4.5 3.3 v -3.3 a 3 3 0 0 1 -3 -3 z" })
|
|
50120
|
+
});
|
|
50121
|
+
}
|
|
50122
|
+
/**
|
|
49942
50123
|
* Group one document's marks by the page they belong to.
|
|
49943
50124
|
* @param marks - every mark of the document.
|
|
49944
50125
|
* @returns the marks of each page; a mark without a page is on page 1.
|
|
@@ -49980,6 +50161,17 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
49980
50161
|
const { createAutosave, annotationSignature, autosaveDelayMs } = props;
|
|
49981
50162
|
const bytes = props.content?.kind === "bytes" ? props.content.data : void 0;
|
|
49982
50163
|
const observerRef = (0, react.useRef)(null);
|
|
50164
|
+
/** The panel itself, so the wheel can be followed where it lands. */
|
|
50165
|
+
const hostRef = (0, react.useRef)(null);
|
|
50166
|
+
/**
|
|
50167
|
+
* The node the page bodies draw their floating chrome into.
|
|
50168
|
+
*
|
|
50169
|
+
* The pane, not the page: a bar mounted inside a page scrolls with that page, and
|
|
50170
|
+
* and it must not move with a page: a bar mounted inside one would scroll away.
|
|
50171
|
+
* as state because it has to be a node React has committed before a body can
|
|
50172
|
+
* portal into it.
|
|
50173
|
+
*/
|
|
50174
|
+
const [chromeHost, setChromeHost] = (0, react.useState)(null);
|
|
49983
50175
|
const [pdf, setPdf] = (0, react.useState)(null);
|
|
49984
50176
|
/** The page the reader asked for: the number in the toolbar follows this one. */
|
|
49985
50177
|
const [requested, setRequested] = (0, react.useState)(1);
|
|
@@ -49994,6 +50186,36 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
49994
50186
|
const [createdAt, setCreatedAt] = (0, react.useState)(null);
|
|
49995
50187
|
const [stale, setStale] = (0, react.useState)(false);
|
|
49996
50188
|
/**
|
|
50189
|
+
* How many times the document has been cleared.
|
|
50190
|
+
*
|
|
50191
|
+
* A page body seeds its marks once, when it mounts, so clearing the document has
|
|
50192
|
+
* to remount the page it is still showing — otherwise the body keeps listing the
|
|
50193
|
+
* marks that were just delivered and deleted.
|
|
50194
|
+
*/
|
|
50195
|
+
const [generation, setGeneration] = (0, react.useState)(0);
|
|
50196
|
+
/**
|
|
50197
|
+
* The card open under the bar, and the mark it was opened about.
|
|
50198
|
+
*
|
|
50199
|
+
* Both live here rather than in a page body: paging away and back mounts a fresh
|
|
50200
|
+
* body, and state that travelled with it would shut the list and put the comment
|
|
50201
|
+
* pill away at every page boundary. One document, one list.
|
|
50202
|
+
*/
|
|
50203
|
+
const [openPanel, setOpenPanel] = (0, react.useState)(null);
|
|
50204
|
+
const [selectedId, setSelectedId] = (0, react.useState)(null);
|
|
50205
|
+
/**
|
|
50206
|
+
* How much the whole document is magnified.
|
|
50207
|
+
*
|
|
50208
|
+
* The document is one picture in several sheets, so the factor belongs here and
|
|
50209
|
+
* not in a page: a page body that kept its own would drop the magnification the
|
|
50210
|
+
* moment the reader turned to the next page, and every page would have to be
|
|
50211
|
+
* zoomed again. Held at this level, a pinch on page 3 is a pinch on the document.
|
|
50212
|
+
*/
|
|
50213
|
+
const [zoom, setZoom] = (0, react.useState)("fit");
|
|
50214
|
+
/** What the page box holds while the reader is typing in it. */
|
|
50215
|
+
const [pageText, setPageText] = (0, react.useState)("1");
|
|
50216
|
+
/** Whether the page number is a box right now rather than a label. */
|
|
50217
|
+
const [editingPage, setEditingPage] = (0, react.useState)(false);
|
|
50218
|
+
/**
|
|
49997
50219
|
* How large the numbers are drawn across the whole document.
|
|
49998
50220
|
*
|
|
49999
50221
|
* One style for every page, because the numbers have to read as one set: a page
|
|
@@ -50003,8 +50225,14 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
50003
50225
|
const [busy, setBusy] = (0, react.useState)(null);
|
|
50004
50226
|
const [status, setStatus] = (0, react.useState)(null);
|
|
50005
50227
|
const pageCount = pdf === null ? 0 : pdf.pageCount;
|
|
50006
|
-
/**
|
|
50007
|
-
|
|
50228
|
+
/**
|
|
50229
|
+
* The width a raster is asked for: the pane's, stepped, times the magnification.
|
|
50230
|
+
*
|
|
50231
|
+
* The factor goes in here rather than into the slot, because a slot widened around
|
|
50232
|
+
* an unzoomed bitmap is a blurred page: the pixels have to be there for the size
|
|
50233
|
+
* the reader asked for.
|
|
50234
|
+
*/
|
|
50235
|
+
const renderWidth = Math.ceil(paneWidth * (zoom === "fit" ? 1 : zoom) / RENDER_WIDTH_STEP) * RENDER_WIDTH_STEP;
|
|
50008
50236
|
/**
|
|
50009
50237
|
* The seats this document acts through.
|
|
50010
50238
|
*
|
|
@@ -50074,6 +50302,8 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
50074
50302
|
* unmount, which is where the observer goes.
|
|
50075
50303
|
*/
|
|
50076
50304
|
const attachHost = (0, react.useCallback)((element) => {
|
|
50305
|
+
hostRef.current = element;
|
|
50306
|
+
setChromeHost(element);
|
|
50077
50307
|
observerRef.current?.disconnect();
|
|
50078
50308
|
observerRef.current = null;
|
|
50079
50309
|
if (element === null) return;
|
|
@@ -50084,6 +50314,67 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
50084
50314
|
observer.observe(element);
|
|
50085
50315
|
observerRef.current = observer;
|
|
50086
50316
|
}, []);
|
|
50317
|
+
/** The only place a page number is decided: requests are clamped, not trusted. */
|
|
50318
|
+
const goTo = (0, react.useCallback)((next) => {
|
|
50319
|
+
setRequested(() => clampPage(next, pageCount));
|
|
50320
|
+
}, [pageCount]);
|
|
50321
|
+
/** Take the page number the box holds, if it holds one. */
|
|
50322
|
+
const commitPage = (0, react.useCallback)(() => {
|
|
50323
|
+
const next = parsePageInput(pageText, pageCount);
|
|
50324
|
+
if (next === null) {
|
|
50325
|
+
setPageText(String(requested));
|
|
50326
|
+
return;
|
|
50327
|
+
}
|
|
50328
|
+
goTo(next);
|
|
50329
|
+
setPageText(String(next));
|
|
50330
|
+
}, [
|
|
50331
|
+
goTo,
|
|
50332
|
+
pageCount,
|
|
50333
|
+
pageText,
|
|
50334
|
+
requested
|
|
50335
|
+
]);
|
|
50336
|
+
/** Enter commits the box; Escape puts the box back to the page on screen. */
|
|
50337
|
+
const submitPage = (0, react.useCallback)((event) => {
|
|
50338
|
+
if (event.key === "Enter") {
|
|
50339
|
+
event.preventDefault();
|
|
50340
|
+
commitPage();
|
|
50341
|
+
setEditingPage(false);
|
|
50342
|
+
return;
|
|
50343
|
+
}
|
|
50344
|
+
if (event.key === "Escape") {
|
|
50345
|
+
event.preventDefault();
|
|
50346
|
+
setPageText(String(requested));
|
|
50347
|
+
setEditingPage(false);
|
|
50348
|
+
}
|
|
50349
|
+
}, [commitPage, requested]);
|
|
50350
|
+
/** The page number the toolbar states: the requested one, clamped to the document. */
|
|
50351
|
+
const stated = clampPage(requested, pageCount);
|
|
50352
|
+
/**
|
|
50353
|
+
* Turn to whatever page a keystroke asks for.
|
|
50354
|
+
*
|
|
50355
|
+
* The keys are read here rather than on the page body because paging belongs to
|
|
50356
|
+
* the document: the body annotates one page and has no idea there are others, and
|
|
50357
|
+
* a keystroke it swallowed would never reach the document at all.
|
|
50358
|
+
*/
|
|
50359
|
+
(0, react.useEffect)(() => {
|
|
50360
|
+
if (pdf === null) return;
|
|
50361
|
+
const onKeyDown = (event) => {
|
|
50362
|
+
if (typingIn(event.target)) return;
|
|
50363
|
+
const turn = keyedTurn(event.key, event);
|
|
50364
|
+
if (turn === null) return;
|
|
50365
|
+
event.preventDefault();
|
|
50366
|
+
goTo(keyedPage(turn, stated, pageCount));
|
|
50367
|
+
};
|
|
50368
|
+
window.addEventListener("keydown", onKeyDown);
|
|
50369
|
+
return () => {
|
|
50370
|
+
window.removeEventListener("keydown", onKeyDown);
|
|
50371
|
+
};
|
|
50372
|
+
}, [
|
|
50373
|
+
goTo,
|
|
50374
|
+
pageCount,
|
|
50375
|
+
pdf,
|
|
50376
|
+
stated
|
|
50377
|
+
]);
|
|
50087
50378
|
(0, react.useEffect)(() => {
|
|
50088
50379
|
if (pdf === null || renderWidth < MIN_PANE_WIDTH) return;
|
|
50089
50380
|
let live = true;
|
|
@@ -50107,10 +50398,27 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
50107
50398
|
requested,
|
|
50108
50399
|
renderWidth
|
|
50109
50400
|
]);
|
|
50110
|
-
/**
|
|
50111
|
-
|
|
50112
|
-
|
|
50113
|
-
|
|
50401
|
+
/**
|
|
50402
|
+
* Drop one mark from wherever in the document it was drawn.
|
|
50403
|
+
*
|
|
50404
|
+
* The list shows the whole file, so its delete button can name a mark on a page
|
|
50405
|
+
* this surface is not holding: the owner is the only one that knows every page.
|
|
50406
|
+
* @param id - the mark to remove.
|
|
50407
|
+
*/
|
|
50408
|
+
const removeAnyMark = (0, react.useCallback)((id) => {
|
|
50409
|
+
setMarks((current) => {
|
|
50410
|
+
let changed = false;
|
|
50411
|
+
const next = new Map(current);
|
|
50412
|
+
for (const [page, pageMarks] of current) {
|
|
50413
|
+
const kept = pageMarks.filter((mark) => mark.id !== id);
|
|
50414
|
+
if (kept.length !== pageMarks.length) {
|
|
50415
|
+
changed = true;
|
|
50416
|
+
next.set(page, kept);
|
|
50417
|
+
}
|
|
50418
|
+
}
|
|
50419
|
+
return changed ? next : current;
|
|
50420
|
+
});
|
|
50421
|
+
}, []);
|
|
50114
50422
|
/** Keep one page's unsaved edits while another page is on screen. */
|
|
50115
50423
|
const remember = (0, react.useCallback)((edited, draft) => {
|
|
50116
50424
|
setMarks((current) => current.get(edited) === draft.marks ? current : new Map(current).set(edited, draft.marks));
|
|
@@ -50250,6 +50558,7 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
50250
50558
|
setSummary("");
|
|
50251
50559
|
setCreatedAt(null);
|
|
50252
50560
|
setStale(false);
|
|
50561
|
+
setGeneration((current) => current + 1);
|
|
50253
50562
|
}, [address]);
|
|
50254
50563
|
/**
|
|
50255
50564
|
* Save the document's annotation and hand it to the agent, or into the draft.
|
|
@@ -50319,44 +50628,143 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
50319
50628
|
pdf,
|
|
50320
50629
|
signature
|
|
50321
50630
|
]);
|
|
50322
|
-
|
|
50631
|
+
/**
|
|
50632
|
+
* Show a mark the reader pressed in the list, wherever in the document it is.
|
|
50633
|
+
*
|
|
50634
|
+
* The list is the document's, so a row can name a mark on a page that is not on
|
|
50635
|
+
* screen. Turning to it is this component's job because the page number is its
|
|
50636
|
+
* state — and the selection is set at the same moment, so the page that arrives
|
|
50637
|
+
* already has the mark selected and its comment pill beside it. Selecting first
|
|
50638
|
+
* and turning second is deliberate: the turn remounts the page body, and a mark
|
|
50639
|
+
* that became selected after the remount would show the pill only on a second
|
|
50640
|
+
* render.
|
|
50641
|
+
*/
|
|
50642
|
+
const revealMark = (0, react.useCallback)((mark) => {
|
|
50643
|
+
setSelectedId(mark.id);
|
|
50644
|
+
if (mark.page !== void 0) goTo(mark.page);
|
|
50645
|
+
}, [goTo]);
|
|
50323
50646
|
const numbering = new Map(allMarks(marks).map((mark, index) => [mark.id, index + 1]));
|
|
50647
|
+
/**
|
|
50648
|
+
* What every page body is told, assembled once.
|
|
50649
|
+
*
|
|
50650
|
+
* One pane shows one page at a time, so there is a single body to hand these to —
|
|
50651
|
+
* but it is assembled here rather than inline so the props a page needs are one
|
|
50652
|
+
* list, not a list that has to be kept in step with a second mount site.
|
|
50653
|
+
*/
|
|
50654
|
+
const pageProps = {
|
|
50655
|
+
resourceAddress: props.resourceAddress,
|
|
50656
|
+
scrollportRef: props.scrollportRef,
|
|
50657
|
+
t,
|
|
50658
|
+
content: void 0,
|
|
50659
|
+
draft: { marks: marks.get(stated) ?? [] },
|
|
50660
|
+
onDraftChange: reportDraft,
|
|
50661
|
+
numbering,
|
|
50662
|
+
summary,
|
|
50663
|
+
onSummaryChange: setSummary,
|
|
50664
|
+
mode,
|
|
50665
|
+
onModeChange: setMode,
|
|
50666
|
+
openPanel,
|
|
50667
|
+
onOpenPanelChange: setOpenPanel,
|
|
50668
|
+
selectedId,
|
|
50669
|
+
onSelectedIdChange: setSelectedId,
|
|
50670
|
+
onRevealMark: revealMark
|
|
50671
|
+
};
|
|
50672
|
+
/**
|
|
50673
|
+
* The paging controls, for the page body's bar to place.
|
|
50674
|
+
*
|
|
50675
|
+
* Drawn here because the page number is this document's state, and placed there
|
|
50676
|
+
* because the bar belongs to the page body: one card holds every control the pane
|
|
50677
|
+
* offers, and none of them move when the page scrolls.
|
|
50678
|
+
*/
|
|
50679
|
+
const pagingSlot = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
50680
|
+
className: "da-docbar-group",
|
|
50681
|
+
children: [
|
|
50682
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
50683
|
+
type: "button",
|
|
50684
|
+
className: "da-dock-btn",
|
|
50685
|
+
"aria-label": t("previousPage"),
|
|
50686
|
+
title: t("previousPage"),
|
|
50687
|
+
disabled: stated <= 1,
|
|
50688
|
+
onClick: () => {
|
|
50689
|
+
goTo(stated - 1);
|
|
50690
|
+
},
|
|
50691
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronLeftIcon, {})
|
|
50692
|
+
}),
|
|
50693
|
+
editingPage ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
50694
|
+
className: "da-page-input",
|
|
50695
|
+
autoFocus: true,
|
|
50696
|
+
inputMode: "numeric",
|
|
50697
|
+
"aria-label": t("pageJump"),
|
|
50698
|
+
value: pageText,
|
|
50699
|
+
onChange: (event) => {
|
|
50700
|
+
setPageText(event.target.value);
|
|
50701
|
+
},
|
|
50702
|
+
onBlur: () => {
|
|
50703
|
+
commitPage();
|
|
50704
|
+
setEditingPage(false);
|
|
50705
|
+
},
|
|
50706
|
+
onKeyDown: submitPage
|
|
50707
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
50708
|
+
type: "button",
|
|
50709
|
+
className: "da-page da-page-btn",
|
|
50710
|
+
title: t("pageJump"),
|
|
50711
|
+
"aria-label": t("pageJump"),
|
|
50712
|
+
onClick: () => {
|
|
50713
|
+
setPageText(String(stated));
|
|
50714
|
+
setEditingPage(true);
|
|
50715
|
+
},
|
|
50716
|
+
children: pdf === null ? `${stated}` : `${stated} / ${pageCount}`
|
|
50717
|
+
}),
|
|
50718
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
50719
|
+
type: "button",
|
|
50720
|
+
className: "da-dock-btn",
|
|
50721
|
+
"aria-label": t("nextPage"),
|
|
50722
|
+
title: t("nextPage"),
|
|
50723
|
+
disabled: pdf !== null && stated >= pageCount,
|
|
50724
|
+
onClick: () => {
|
|
50725
|
+
goTo(stated + 1);
|
|
50726
|
+
},
|
|
50727
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChevronRightIcon, {})
|
|
50728
|
+
})
|
|
50729
|
+
]
|
|
50730
|
+
}) });
|
|
50324
50731
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
50325
50732
|
className: "da-pdf",
|
|
50326
50733
|
ref: attachHost,
|
|
50327
50734
|
children: [
|
|
50328
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
50329
|
-
className: "da-
|
|
50735
|
+
shown !== null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
50736
|
+
className: "da-bar",
|
|
50737
|
+
"data-static": "true",
|
|
50738
|
+
role: "toolbar",
|
|
50739
|
+
"aria-label": t("pagerLabel"),
|
|
50330
50740
|
children: [
|
|
50741
|
+
pagingSlot,
|
|
50742
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "da-spacer" }),
|
|
50743
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
50744
|
+
className: "da-dock-sep",
|
|
50745
|
+
"aria-hidden": true
|
|
50746
|
+
}),
|
|
50331
50747
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
50332
50748
|
type: "button",
|
|
50333
|
-
className: "da-btn",
|
|
50334
|
-
disabled:
|
|
50749
|
+
className: "da-dock-btn",
|
|
50750
|
+
disabled: busy !== null,
|
|
50751
|
+
"aria-label": t("sendToAgent"),
|
|
50752
|
+
title: t("sendToAgent"),
|
|
50335
50753
|
onClick: () => {
|
|
50336
|
-
|
|
50754
|
+
run("agent");
|
|
50337
50755
|
},
|
|
50338
|
-
children:
|
|
50339
|
-
}),
|
|
50340
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
50341
|
-
className: "da-page",
|
|
50342
|
-
children: pdf === null ? `${requested}` : `${requested} / ${pageCount}`
|
|
50756
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SendIcon, {})
|
|
50343
50757
|
}),
|
|
50344
50758
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
50345
50759
|
type: "button",
|
|
50346
|
-
className: "da-btn",
|
|
50347
|
-
disabled:
|
|
50760
|
+
className: "da-dock-btn",
|
|
50761
|
+
disabled: busy !== null,
|
|
50762
|
+
"aria-label": t("sendToComposer"),
|
|
50763
|
+
title: t("sendToComposer"),
|
|
50348
50764
|
onClick: () => {
|
|
50349
|
-
|
|
50765
|
+
run("composer");
|
|
50350
50766
|
},
|
|
50351
|
-
children:
|
|
50352
|
-
}),
|
|
50353
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { className: "da-spacer" }),
|
|
50354
|
-
pdf === null ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
50355
|
-
className: "da-page",
|
|
50356
|
-
children: t("documentScope", {
|
|
50357
|
-
pages: pageCount,
|
|
50358
|
-
marks: total
|
|
50359
|
-
})
|
|
50767
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ChatIcon, {})
|
|
50360
50768
|
})
|
|
50361
50769
|
]
|
|
50362
50770
|
}),
|
|
@@ -50381,39 +50789,17 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
50381
50789
|
children: t("pdfRendering")
|
|
50382
50790
|
})
|
|
50383
50791
|
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(AnnotatorBody, {
|
|
50384
|
-
|
|
50385
|
-
scrollportRef: props.scrollportRef,
|
|
50386
|
-
t,
|
|
50387
|
-
content: void 0,
|
|
50792
|
+
...pageProps,
|
|
50388
50793
|
page: shown.bitmap,
|
|
50389
50794
|
draft: { marks: marks.get(shown.page) ?? [] },
|
|
50390
|
-
|
|
50391
|
-
|
|
50392
|
-
|
|
50393
|
-
|
|
50394
|
-
|
|
50395
|
-
|
|
50396
|
-
|
|
50397
|
-
|
|
50398
|
-
className: "da-send",
|
|
50399
|
-
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
50400
|
-
type: "button",
|
|
50401
|
-
className: "da-btn",
|
|
50402
|
-
disabled: busy !== null,
|
|
50403
|
-
onClick: () => {
|
|
50404
|
-
run("agent");
|
|
50405
|
-
},
|
|
50406
|
-
children: t("sendToAgent")
|
|
50407
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
50408
|
-
type: "button",
|
|
50409
|
-
className: "da-btn",
|
|
50410
|
-
disabled: busy !== null,
|
|
50411
|
-
onClick: () => {
|
|
50412
|
-
run("composer");
|
|
50413
|
-
},
|
|
50414
|
-
children: t("sendToComposer")
|
|
50415
|
-
})]
|
|
50416
|
-
})
|
|
50795
|
+
chromeHost,
|
|
50796
|
+
pagingSlot,
|
|
50797
|
+
onSend: run,
|
|
50798
|
+
zoom,
|
|
50799
|
+
onZoom: setZoom,
|
|
50800
|
+
documentMarks: allMarks(marks),
|
|
50801
|
+
onDocumentRemove: removeAnyMark
|
|
50802
|
+
}, `${generation}:${shown.page}`)
|
|
50417
50803
|
]
|
|
50418
50804
|
});
|
|
50419
50805
|
}
|
|
@@ -50426,9 +50812,10 @@ fn fs_main(in : VertexOutput) -> @location(0) vec4<f32> {
|
|
|
50426
50812
|
*/
|
|
50427
50813
|
async function renderPage(pdf, page, paneWidth) {
|
|
50428
50814
|
const size = await pdf.size(page);
|
|
50429
|
-
const
|
|
50430
|
-
const
|
|
50431
|
-
const
|
|
50815
|
+
const room = Math.max(1, paneWidth - PANE_INSET);
|
|
50816
|
+
const shown = Math.min(room, size.width * FIT_CEILING);
|
|
50817
|
+
const density = Math.min(MAX_DENSITY, Math.max(1, globalThis.devicePixelRatio || 1));
|
|
50818
|
+
const rendered = await pdf.render(page, shown * density * SUPERSAMPLE / size.width);
|
|
50432
50819
|
return {
|
|
50433
50820
|
dataUrl: rendered.dataUrl,
|
|
50434
50821
|
width: rendered.width,
|
package/lib/index.js
CHANGED
|
@@ -693,11 +693,11 @@ async function resolveTarget(address, deps) {
|
|
|
693
693
|
ok: false,
|
|
694
694
|
message: `not a file address: ${address}`
|
|
695
695
|
};
|
|
696
|
+
const sessionId = parsed.sessionId;
|
|
697
|
+
const cwd = sessionId === void 0 ? void 0 : await deps.sessionCwd(sessionId);
|
|
696
698
|
let absolute;
|
|
697
699
|
if (isAbsolute(parsed.path)) absolute = parsed.path;
|
|
698
700
|
else {
|
|
699
|
-
const sessionId = parsed.sessionId;
|
|
700
|
-
const cwd = sessionId === void 0 ? void 0 : await deps.sessionCwd(sessionId);
|
|
701
701
|
if (cwd === void 0) return {
|
|
702
702
|
ok: false,
|
|
703
703
|
message: `cannot resolve this Session's directory for a relative path: ${address}`
|
|
@@ -730,7 +730,8 @@ async function resolveTarget(address, deps) {
|
|
|
730
730
|
target: {
|
|
731
731
|
address,
|
|
732
732
|
path: absolute,
|
|
733
|
-
mediaType
|
|
733
|
+
mediaType,
|
|
734
|
+
workspace: cwd ?? null
|
|
734
735
|
}
|
|
735
736
|
};
|
|
736
737
|
}
|
package/market/index.json
CHANGED
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-figmark",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"private": false,
|
|
5
|
-
"description": "DSH
|
|
5
|
+
"description": "DSH 插件:在右侧栏预览里对科研配图、SVG、PDF、HTML 直接圈画批注,标注写进侧车文件并可一键交给智能体改图源",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"icon": "./icon.svg",
|
|
8
8
|
"keywords": [
|