@plumeai/plugin-sdk 0.0.0-stage → 0.1.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.
Files changed (210) hide show
  1. package/LICENSE +21 -0
  2. package/bin/plume-plugin.mjs +70 -0
  3. package/dist/client/styles/src/index.d.ts +123 -0
  4. package/dist/client/ui/src/avatar/AgentAvatar.d.ts +42 -0
  5. package/dist/client/ui/src/avatar/AvatarPile.d.ts +36 -0
  6. package/dist/client/ui/src/avatar/AvatarStack.d.ts +26 -0
  7. package/dist/client/ui/src/avatar/art.d.ts +32 -0
  8. package/dist/client/ui/src/fileIcon.d.ts +33 -0
  9. package/dist/client/ui/src/hooks/useCopied.d.ts +16 -0
  10. package/dist/client/ui/src/hooks/useLiveRefresh.d.ts +22 -0
  11. package/dist/client/ui/src/hooks/useNarrow.d.ts +16 -0
  12. package/dist/client/ui/src/i18n/context.d.ts +17 -0
  13. package/dist/client/ui/src/i18n/index.d.ts +2 -0
  14. package/dist/client/ui/src/i18n/list.d.ts +20 -0
  15. package/dist/client/ui/src/i18n/locales.d.ts +10 -0
  16. package/dist/client/ui/src/i18n/messages/en.d.ts +1418 -0
  17. package/dist/client/ui/src/i18n/messages/index.d.ts +6 -0
  18. package/dist/client/ui/src/i18n/messages/zh-CN.d.ts +1263 -0
  19. package/dist/client/ui/src/i18n/plural.d.ts +16 -0
  20. package/dist/client/ui/src/i18n/translate.d.ts +43 -0
  21. package/dist/client/ui/src/icons/index.d.ts +5 -0
  22. package/dist/client/ui/src/image/AnnotateCanvas.d.ts +29 -0
  23. package/dist/client/ui/src/image/AnnotateToolbar.d.ts +73 -0
  24. package/dist/client/ui/src/image/Annotator.d.ts +154 -0
  25. package/dist/client/ui/src/image/annotate.d.ts +227 -0
  26. package/dist/client/ui/src/image/paint.d.ts +35 -0
  27. package/dist/client/ui/src/image/screenshot-geometry.d.ts +112 -0
  28. package/dist/client/ui/src/image/useBarDrag.d.ts +31 -0
  29. package/dist/client/ui/src/image/useMovableBar.d.ts +26 -0
  30. package/dist/client/ui/src/image/useToolbarPlacement.d.ts +42 -0
  31. package/dist/client/ui/src/index.d.ts +140 -0
  32. package/dist/client/ui/src/inputs/ColorField.d.ts +9 -0
  33. package/dist/client/ui/src/inputs/ComposerToggle.d.ts +23 -0
  34. package/dist/client/ui/src/inputs/DateRangeField.d.ts +25 -0
  35. package/dist/client/ui/src/inputs/NativeField.d.ts +5 -0
  36. package/dist/client/ui/src/inputs/SearchField.d.ts +21 -0
  37. package/dist/client/ui/src/inputs/Segmented.d.ts +23 -0
  38. package/dist/client/ui/src/inputs/Select.d.ts +32 -0
  39. package/dist/client/ui/src/inputs/ShortcutRecorder.d.ts +8 -0
  40. package/dist/client/ui/src/inputs/Slider.d.ts +13 -0
  41. package/dist/client/ui/src/inputs/TextArea.d.ts +30 -0
  42. package/dist/client/ui/src/inputs/Toggle.d.ts +10 -0
  43. package/dist/client/ui/src/inputs/WorktreeToggle.d.ts +19 -0
  44. package/dist/client/ui/src/inputs/day-range.d.ts +57 -0
  45. package/dist/client/ui/src/inputs/pickers.d.ts +40 -0
  46. package/dist/client/ui/src/inputs/useFieldFade.d.ts +1 -0
  47. package/dist/client/ui/src/keyboard.d.ts +21 -0
  48. package/dist/client/ui/src/layout/ApprovalFrame.d.ts +33 -0
  49. package/dist/client/ui/src/layout/Collapse.d.ts +9 -0
  50. package/dist/client/ui/src/layout/CopyMark.d.ts +19 -0
  51. package/dist/client/ui/src/layout/Disclosure.d.ts +66 -0
  52. package/dist/client/ui/src/layout/ErrorBoundary.d.ts +26 -0
  53. package/dist/client/ui/src/layout/PageTopRow.d.ts +19 -0
  54. package/dist/client/ui/src/layout/PanelEmpty.d.ts +17 -0
  55. package/dist/client/ui/src/layout/ResizeHandle.d.ts +24 -0
  56. package/dist/client/ui/src/layout/RetainedViews.d.ts +32 -0
  57. package/dist/client/ui/src/layout/Settings.d.ts +41 -0
  58. package/dist/client/ui/src/layout/Splitter.d.ts +25 -0
  59. package/dist/client/ui/src/lib/agent-avatar.d.ts +58 -0
  60. package/dist/client/ui/src/lib/code/font-stack.d.ts +12 -0
  61. package/dist/client/ui/src/lib/code/themes.d.ts +35 -0
  62. package/dist/client/ui/src/lib/date-format.d.ts +8 -0
  63. package/dist/client/ui/src/lib/dispatches.d.ts +8 -0
  64. package/dist/client/ui/src/lib/hour-style.d.ts +13 -0
  65. package/dist/client/ui/src/lib/number-draft.d.ts +24 -0
  66. package/dist/client/ui/src/lib/relative-time.d.ts +18 -0
  67. package/dist/client/ui/src/lib/tiling.d.ts +89 -0
  68. package/dist/client/ui/src/lib/typing.d.ts +19 -0
  69. package/dist/client/ui/src/motion/RollWords.d.ts +26 -0
  70. package/dist/client/ui/src/motion/RollingText.d.ts +43 -0
  71. package/dist/client/ui/src/motion/TypedText.d.ts +9 -0
  72. package/dist/client/ui/src/motion/freeze.d.ts +22 -0
  73. package/dist/client/ui/src/motion/held.d.ts +17 -0
  74. package/dist/client/ui/src/motion/loaders.d.ts +65 -0
  75. package/dist/client/ui/src/motion/reduced.d.ts +13 -0
  76. package/dist/client/ui/src/motion/ticked.d.ts +24 -0
  77. package/dist/client/ui/src/motion/ticker.d.ts +31 -0
  78. package/dist/client/ui/src/motion/tokens.d.ts +29 -0
  79. package/dist/client/ui/src/motion/useNow.d.ts +6 -0
  80. package/dist/client/ui/src/overlay/Confirm.d.ts +96 -0
  81. package/dist/client/ui/src/overlay/ContextMenu.d.ts +40 -0
  82. package/dist/client/ui/src/overlay/Dialog.d.ts +60 -0
  83. package/dist/client/ui/src/overlay/Menu.d.ts +90 -0
  84. package/dist/client/ui/src/overlay/Overlay.d.ts +17 -0
  85. package/dist/client/ui/src/overlay/Popover.d.ts +127 -0
  86. package/dist/client/ui/src/overlay/Tooltip.d.ts +14 -0
  87. package/dist/client/ui/src/overlay/afterimage.d.ts +20 -0
  88. package/dist/client/ui/src/overlay/chrome-top.d.ts +8 -0
  89. package/dist/client/ui/src/overlay/hover-layers.d.ts +41 -0
  90. package/dist/client/ui/src/overlay/portal.d.ts +21 -0
  91. package/dist/client/ui/src/overlay/scrims.d.ts +20 -0
  92. package/dist/client/ui/src/overlay/tooltip-attribute.d.ts +62 -0
  93. package/dist/client/ui/src/primitives/Button.d.ts +64 -0
  94. package/dist/client/ui/src/primitives/Caret.d.ts +25 -0
  95. package/dist/client/ui/src/primitives/ChoiceMark.d.ts +12 -0
  96. package/dist/client/ui/src/primitives/ClosableTab.d.ts +23 -0
  97. package/dist/client/ui/src/primitives/CountUp.d.ts +21 -0
  98. package/dist/client/ui/src/primitives/GitHubMark.d.ts +12 -0
  99. package/dist/client/ui/src/primitives/IconButton.d.ts +55 -0
  100. package/dist/client/ui/src/primitives/MarkNumber.d.ts +12 -0
  101. package/dist/client/ui/src/primitives/RowDeleteButton.d.ts +6 -0
  102. package/dist/client/ui/src/primitives/Skeleton.d.ts +36 -0
  103. package/dist/client/ui/src/primitives/TabStrip.d.ts +29 -0
  104. package/dist/client/ui/src/primitives/Text.d.ts +341 -0
  105. package/dist/client/ui/src/primitives/TimeAgo.d.ts +13 -0
  106. package/dist/client/ui/src/primitives/useCountUp.d.ts +23 -0
  107. package/dist/client/ui/src/row/FlowRow.d.ts +31 -0
  108. package/dist/client/ui/src/row/HoverRow.d.ts +28 -0
  109. package/dist/client/ui/src/scroll/BackToLatest.d.ts +19 -0
  110. package/dist/client/ui/src/scroll/OverlayScrollbar.d.ts +9 -0
  111. package/dist/client/ui/src/scroll/ScrollText.d.ts +24 -0
  112. package/dist/client/ui/src/scroll/Scroller.d.ts +66 -0
  113. package/dist/client/ui/src/scroll/Sideways.d.ts +30 -0
  114. package/dist/client/ui/src/scroll/fade.d.ts +2 -0
  115. package/dist/client/ui/src/scroll/follow.d.ts +157 -0
  116. package/dist/client/ui/src/scroll/measure-batch.d.ts +13 -0
  117. package/dist/client/ui/src/scroll/memory.d.ts +22 -0
  118. package/dist/client/ui/src/scroll/signature.d.ts +24 -0
  119. package/dist/client/ui/src/scroll/useFollowBottom.d.ts +80 -0
  120. package/dist/client/ui/src/scroll/useSideways.d.ts +29 -0
  121. package/dist/contract/src/app.d.ts +16 -0
  122. package/dist/contract/src/changes.d.ts +50 -0
  123. package/dist/contract/src/diagnostics.d.ts +86 -0
  124. package/dist/contract/src/index.d.ts +18 -0
  125. package/dist/contract/src/plugin.d.ts +1391 -0
  126. package/dist/contract/src/resources.d.ts +79 -0
  127. package/dist/contract/src/service.d.ts +806 -0
  128. package/dist/contract/src/ui.d.ts +1039 -0
  129. package/dist/contract/src/usage.d.ts +102 -0
  130. package/dist/core/llm/ai/anthropic-messages-request.d.ts +82 -0
  131. package/dist/core/llm/ai/anthropic-messages.d.ts +43 -0
  132. package/dist/core/llm/ai/cache-routing.d.ts +53 -0
  133. package/dist/core/llm/ai/chat-delta.d.ts +107 -0
  134. package/dist/core/llm/ai/compat-key.d.ts +19 -0
  135. package/dist/core/llm/ai/endpoint.d.ts +57 -0
  136. package/dist/core/llm/ai/failure.d.ts +181 -0
  137. package/dist/core/llm/ai/index.d.ts +16 -0
  138. package/dist/core/llm/ai/openai-chat-completions-request.d.ts +49 -0
  139. package/dist/core/llm/ai/openai-chat-completions.d.ts +37 -0
  140. package/dist/core/llm/ai/openai-responses-request.d.ts +63 -0
  141. package/dist/core/llm/ai/openai-responses.d.ts +11 -0
  142. package/dist/core/llm/ai/prefix-fingerprint.d.ts +20 -0
  143. package/dist/core/llm/ai/reasoning-compat.d.ts +69 -0
  144. package/dist/core/llm/ai/request-params-compat.d.ts +39 -0
  145. package/dist/core/llm/ai/retry.d.ts +130 -0
  146. package/dist/core/llm/ai/sanitize-history.d.ts +23 -0
  147. package/dist/core/llm/ai/think-tag.d.ts +37 -0
  148. package/dist/core/llm/ai/thinking-options.d.ts +35 -0
  149. package/dist/core/llm/ai/tool-names.d.ts +17 -0
  150. package/dist/core/llm/ai/tool-pairing-compat.d.ts +53 -0
  151. package/dist/core/llm/ai/tool-result-images-compat.d.ts +24 -0
  152. package/dist/core/llm/ai/tool-schema.d.ts +20 -0
  153. package/dist/core/llm/ai/usage-fields.d.ts +30 -0
  154. package/dist/core/llm/ai/vendors/index.d.ts +20 -0
  155. package/dist/core/llm/ai/vendors/thinking.d.ts +39 -0
  156. package/dist/core/llm/ai/vendors/types.d.ts +47 -0
  157. package/dist/core/llm/config/image-models.d.ts +34 -0
  158. package/dist/core/llm/config/retry-policy.d.ts +35 -0
  159. package/dist/core/llm/images/chat-images.d.ts +11 -0
  160. package/dist/core/llm/images/dimensions.d.ts +15 -0
  161. package/dist/core/llm/images/generate.d.ts +15 -0
  162. package/dist/core/llm/images/openai-images.d.ts +11 -0
  163. package/dist/core/llm/images/quota.d.ts +9 -0
  164. package/dist/core/llm/images/shape.d.ts +44 -0
  165. package/dist/core/llm/images/types.d.ts +118 -0
  166. package/dist/core/llm/model-catalog-format.d.ts +50 -0
  167. package/dist/core/llm/model-catalog.d.ts +51 -0
  168. package/dist/core/llm/types/message.d.ts +450 -0
  169. package/dist/core/llm/types/provider.d.ts +169 -0
  170. package/dist/core/llm/types/tool.d.ts +23 -0
  171. package/dist/core/llm/types.d.ts +10 -0
  172. package/dist/core/llm/utils/pricing.d.ts +18 -0
  173. package/dist/core/llm/utils/record.d.ts +2 -0
  174. package/dist/core/llm/utils/sse.d.ts +69 -0
  175. package/dist/infra/rpc/src/endpoint.d.ts +36 -0
  176. package/dist/infra/rpc/src/index.d.ts +2 -0
  177. package/dist/infra/rpc/src/rpc.d.ts +54 -0
  178. package/dist/theme.css +336 -0
  179. package/dist/ui.d.ts +2 -0
  180. package/lib/build.mjs +143 -0
  181. package/lib/check.mjs +23 -0
  182. package/lib/dev.mjs +52 -0
  183. package/lib/icons.mjs +2 -0
  184. package/lib/init.mjs +109 -0
  185. package/lib/install.mjs +80 -0
  186. package/lib/link.mjs +44 -0
  187. package/lib/lint/i18n.mjs +149 -0
  188. package/lib/lint/imports.mjs +83 -0
  189. package/lib/lint/index.mjs +54 -0
  190. package/lib/lint/styles.mjs +160 -0
  191. package/lib/manifest.d.mts +9 -0
  192. package/lib/manifest.mjs +99 -0
  193. package/lib/targets.mjs +60 -0
  194. package/package.json +34 -4
  195. package/skill/SKILL.md +150 -0
  196. package/skill/references/components.md +2830 -0
  197. package/skill/references/logic.md +526 -0
  198. package/skill/references/style-i18n.md +70 -0
  199. package/skill/references/ui.md +532 -0
  200. package/template/AGENTS.md +17 -0
  201. package/template/CLAUDE.md +1 -0
  202. package/template/README.md +12 -0
  203. package/template/gitignore +2 -0
  204. package/template/src/index.ts +28 -0
  205. package/template/src/messages.ts +16 -0
  206. package/template/src/types.ts +8 -0
  207. package/template/src/ui.tsx +30 -0
  208. package/template/tsconfig.json +7 -0
  209. package/tsconfig.json +25 -0
  210. package/README.md +0 -3
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Which form of a sentence a number takes, in the language the sentence is being said in.
3
+ *
4
+ * A catalogue entry used to be one string with the count pasted in, so the archive said
5
+ * "1 conversations". English wants two forms and Chinese wants none; `Intl.PluralRules` knows which
6
+ * CLDR category a number falls in, so a catalogue only gives one sentence per category its language
7
+ * uses — see `PluralForms` next to `MessageCatalog`.
8
+ *
9
+ * The count is always the variable `{n}`. That keeps "which number decides" out of the catalogue: a
10
+ * sentence with two counts in it takes its form from the one called `n`.
11
+ */
12
+ import type { PluralForms, ResolvedUiLocale } from "./messages/index.ts";
13
+ /** The plural category `count` falls in for `locale`; `other` for anything that is not a finite number. */
14
+ export declare function pluralCategory(locale: ResolvedUiLocale, count: number): Intl.LDMLPluralRule;
15
+ /** The sentence `message` says for the count `n`. A plain string is the same sentence for every number. */
16
+ export declare function pluralForm(locale: ResolvedUiLocale, message: string | PluralForms, n: unknown): string;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Translating outside a component, for the code that has no hook to reach for.
3
+ *
4
+ * `useI18n` covers everything that renders. It does not cover the rest of what a person reads: the
5
+ * notices the store raises when a send fails, the sentence `hiccup.ts` builds for a dropped
6
+ * connection, the labels a plain function returns for a menu it does not own. Those had the language
7
+ * written into them, so switching the interface to English left them in Chinese — which is most of
8
+ * what "the settings are translated but the app is not" turned out to mean.
9
+ *
10
+ * Threading a `t` through every one of those call sites was the alternative, and it is worse than it
11
+ * sounds: `describeHiccup` would take a translator to return a string, and so would everything that
12
+ * calls it, all the way up to a component — for a value that depends on one global thing, the
13
+ * language the window is set to. So the language lives here instead, and `I18nProvider` keeps it in
14
+ * step; see `setActiveLocale`.
15
+ *
16
+ * The catalogue is the same one the hook reads, and both come down to `translateIn`, so a key cannot
17
+ * mean two different things depending on which of the two found it, and `MessageKey` still makes a
18
+ * missing translation a type error.
19
+ *
20
+ * **Import this file directly from a `.ts` module — not `i18n/index.ts`.** The barrel re-exports
21
+ * `context.tsx`, and a plain-TypeScript module that pulls it in stops being loadable by
22
+ * `node --test --experimental-strip-types`, which does not know what to do with a `.tsx`. It shows
23
+ * up as `ERR_UNKNOWN_FILE_EXTENSION` in a test that has nothing to do with i18n — `syncPlan.ts`'s
24
+ * suite went red that way. Type-only imports from the barrel are fine: they leave no runtime trace.
25
+ */
26
+ import { type MessageKey, type ResolvedUiLocale } from "./messages/index.ts";
27
+ export type MessageVariables = Readonly<Record<string, string | number>>;
28
+ /** Called by `I18nProvider` whenever the resolved language changes. */
29
+ export declare function setActiveLocale(locale: ResolvedUiLocale): void;
30
+ /** What the window is currently set to — for the rare caller that needs to ask. */
31
+ export declare function activeLocale(): ResolvedUiLocale;
32
+ /**
33
+ * One message, in the language the window is set to.
34
+ *
35
+ * Falls back to `zh-CN` for a key a catalogue is missing, which the types make unreachable today and
36
+ * would be the right answer anyway: a sentence in the wrong language beats a key printed at a person.
37
+ */
38
+ export declare function translate(key: MessageKey, variables?: MessageVariables): string;
39
+ /**
40
+ * One message in a language named by the caller — what `translate` and `useI18n().t` both call, so
41
+ * the plural form a count takes is picked the same way on both paths.
42
+ */
43
+ export declare function translateIn(locale: ResolvedUiLocale, key: MessageKey, variables?: MessageVariables): string;
@@ -0,0 +1,5 @@
1
+ /**
2
+ * 所有 lucide 图标的入口:从这里 import,不要直接从 `lucide-react` import(类型除外),换图标库或统一处理
3
+ * 图标时只改这一处。图标一律是 lucide 原版,悬停时不播动画。
4
+ */
5
+ export * from "lucide-react";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * 画布:把标记画出来,以及把指针动作翻译成标记。
3
+ *
4
+ * 从 `Annotator.tsx` 拆出来的第二块。标记是一张形状清单,每帧从清单重画,而不是随手落笔累积
5
+ * 到画布上——那是撤销/重做能成为清单上的一个游标而不是一叠位图的原因,也是撤掉一个步骤徽章之后
6
+ * 其余的会自己重新编号的原因,还是「正在拖的那个矩形」能实时显示的原因:预览和提交走的是同一段
7
+ * 代码,清单加一个临时项而已。
8
+ *
9
+ * 标记存在图片的**原始**像素里;画布的像素数则按它在屏幕上的样子来(见 `backingScale`),不再
10
+ * 等于原图。指针坐标进来时经 `getBoundingClientRect` 换算,那个方法已经把上层舞台的缩放算进去了
11
+ * ——所以在 400% 下画,落点仍然在指针处。缩放在这里只用来定两件事:画布该有多少像素才清楚,以及
12
+ * 选中框、手柄这些「编辑用的东西」该缩回多少才不跟着图一起变大。
13
+ *
14
+ * 工具条为什么不在这里:`AnnotateToolbar.tsx` 的文件头写着。
15
+ */
16
+ import type { Annotator } from "./Annotator.tsx";
17
+ /** 和看图器的 `<img>` 共用,进入编辑时图不会差一个像素。 */
18
+ export declare const STAGE_FIT = "max-h-[86vh] max-w-[86vw]";
19
+ /**
20
+ * @param className 替换画布自己的尺寸和外框,给需要把它摆得分毫不差的调用方。看图器要 `STAGE_FIT`
21
+ * ——按图片自己的像素定大小、以窗口为上限;截图浮层要的正相反,一块钉在它已经选好的矩形上的画布。
22
+ * 不传的话画布按位图尺寸以 CSS 像素排版,在 Retina 屏上是它该盖住的区域的两倍。
23
+ */
24
+ export declare function AnnotateCanvas({ annotator, zoom, className, style }: {
25
+ annotator: Annotator;
26
+ zoom: number;
27
+ className?: string;
28
+ style?: React.CSSProperties;
29
+ }): import("react").JSX.Element;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * 标注工具条:选工具、选颜色、选粗细,以及那几个一次性动作。
3
+ *
4
+ * 从 `Annotator.tsx` 拆出来的——那个文件本来就是三块:一个 hook、一块画布、一条工具条,只是三块住在
5
+ * 一起。工具条这一块和另外两块之间只有四个符号来往,其中三个(`BACKDROPS`、`WEIGHT_LEVELS`,以及这里的
6
+ * 一堆度量与图标表)本来就只有它自己用,跟着搬过来了;剩下 `COLOURS` 两边都用,留在原处导出。
7
+ *
8
+ * 工具条不能住在画布的父节点里:舞台为了缩放带着 transform,而 `position: fixed` 在一个被 transform 过的
9
+ * 祖先里是相对那个祖先固定的。一个跟着图片一起放大、一起滑走的工具条,在 400% 下没法用。
10
+ */
11
+ import { type Annotator } from "./Annotator.tsx";
12
+ export declare function AnnotateToolbar({ annotator, onCancel, onSave, onPin, onDownload, onGrab, grabbing, canReplace, delivers, saveLabel, cancelLabel, requireDirty, style, propertiesSide, }: {
13
+ annotator: Annotator;
14
+ onCancel: () => void;
15
+ onSave: () => void;
16
+ /**
17
+ * 把这张图留在桌面上、浮在一切之上,而不是交出去。
18
+ *
19
+ * 只在有意义的地方给——截图浮层——因为把一张已经开在窗口里的图的一块置顶,是给正在看的东西复制一份。
20
+ */
21
+ onPin?: () => void;
22
+ /** 把这张图写进下载目录。理由同 `onPin`。 */
23
+ onDownload?: () => void;
24
+ /**
25
+ * 这一条要挪窝了:从哪儿按下的,按下时它在哪儿。
26
+ *
27
+ * 挪到哪儿是调用方的事——它飘在一张截好的屏幕上,得留在屏幕里——所以这里只说「开始了」,外加
28
+ * 两个位置。带上 `origin` 而不是让调用方自己去 DOM 里量:这一条的位置只有它自己最清楚,而且
29
+ * 按住按钮触发的那一次是从定时器里发出的,那会儿 React 的合成事件早就没有 `currentTarget` 了。
30
+ *
31
+ * 不传就是不能挪。
32
+ */
33
+ onGrab?: (grab: {
34
+ from: {
35
+ x: number;
36
+ y: number;
37
+ };
38
+ origin: {
39
+ x: number;
40
+ y: number;
41
+ };
42
+ }) => void;
43
+ /** 正在挪,好让整条和它上面每个控件都显示同一个光标。 */
44
+ grabbing?: boolean;
45
+ /**
46
+ * 当前工具的属性气泡往哪边开,以这条工具条为准。
47
+ *
48
+ * `above` 开在工具条上面,`below` 开在下面。由调用方决定,因为只有调用方知道工具条摆在被标注的东西的
49
+ * 哪一边:气泡得往远离它的方向开,否则就把它盖住了。默认 `above`,看图器就是这样——工具条一开始贴着
50
+ * 舞台底边。
51
+ */
52
+ propertiesSide?: "above" | "below";
53
+ /** 保存能不能替换原图,还是只能另出一份副本。 */
54
+ canReplace: boolean;
55
+ /**
56
+ * 副本是交出去接着用,而不是导出。
57
+ *
58
+ * 对话里的一张图被标注,是为了让它照着重画,所以副本进那个对话的输入框:按钮是带勾的「完成」,而不是
59
+ * 一个下载箭头,许诺一个谁也没要的文件。
60
+ */
61
+ delivers?: boolean;
62
+ /** 覆盖保存按钮上的字。不传就按 `canReplace` 的意思说。 */
63
+ saveLabel?: string;
64
+ cancelLabel?: string;
65
+ /**
66
+ * 保存是不是要求先画过东西。
67
+ *
68
+ * 对一张已经存在的图是 true——保存一份没动过的副本只会多出一个一模一样的文件,所以在有东西可存之前
69
+ * 按钮是死的。对截图浮层是 false:那个按钮就是确认这次截图的方式,而一块没有标注的区域是最常见的需求。
70
+ */
71
+ requireDirty?: boolean;
72
+ style?: React.CSSProperties;
73
+ }): import("react").JSX.Element;
@@ -0,0 +1,154 @@
1
+ /**
2
+ * 在图片上面画东西,而不是画进图片里。
3
+ *
4
+ * 标记存成一张形状清单,每次从清单重画,而不是落一笔就累积进画布。正因为这样,撤销和重做才是清单上的
5
+ * 一个游标而不是一叠位图;撤掉一个步骤标号,其余的才会自己重新编号;正在画的形状才能实时显示——还在拖的
6
+ * 矩形每一帧都从同一张清单加一个临时项画出来,预览和提交走的是同一段代码。
7
+ *
8
+ * 画布按图片的**原始**像素开,再由 CSS 缩小。指针坐标进来时经 `getBoundingClientRect` 换算,它已经把
9
+ * 上层舞台的缩放算进去了——所以在 400% 下画,落点仍然在指针处,而这个文件里根本不用出现缩放。
10
+ *
11
+ * 拆成一个 hook、一块画布和一条工具条,是因为工具条不能住在画布的父节点里:舞台为了缩放带着 transform,
12
+ * 而 `position: fixed` 在被 transform 过的祖先里是相对那个祖先固定的,不是相对窗口。一条跟着图片一起
13
+ * 放大、一起滑走的工具条,在 400% 下没法用。
14
+ */
15
+ import { type History, type Shape, type Tool } from "./annotate.ts";
16
+ export declare const COLOURS: string[];
17
+ /**
18
+ * 一张解码好的图,和它的像素尺寸。
19
+ *
20
+ * 到这里的有两种东西,不是同一种对象。看图器标注的是一张能当 URL 用的图,解码成一个 `<img>`;截图浮层
21
+ * 拿到的是屏幕的原始像素,解码成 `ImageBitmap`——因为在主进程里把屏幕编码成 PNG 要花掉截图出现之前的
22
+ * 那段延迟里的 133ms,而截图上的画面是在那段延迟**之前**拍下的,于是这 133ms 里屏幕可能变了,再肉眼可见
23
+ * 地弹回去。
24
+ *
25
+ * `drawImage` 两种都收,分不出区别。不同的是它们报尺寸的方式——`naturalWidth` 对 `width`——所以只在
26
+ * 这里读一次,下游一律用这个形状。
27
+ */
28
+ interface Decoded {
29
+ source: CanvasImageSource;
30
+ width: number;
31
+ height: number;
32
+ }
33
+ export interface Annotator {
34
+ tool: Tool;
35
+ setTool: (tool: Tool) => void;
36
+ colour: string;
37
+ setColour: (colour: string) => void;
38
+ /** 文字底下垫什么;undefined 就是什么都不垫。 */
39
+ backdrop: string | undefined;
40
+ setBackdrop: (backdrop: string | undefined) => void;
41
+ weight: number;
42
+ setWeight: (weight: number) => void;
43
+ undo: () => void;
44
+ redo: () => void;
45
+ clear: () => void;
46
+ canUndo: boolean;
47
+ canRedo: boolean;
48
+ dirty: boolean;
49
+ /** 选中的是哪个标记,或者 null。是当前状态那张清单里的下标。 */
50
+ selected: number | null;
51
+ setSelected: (index: number | null) => void;
52
+ removeSelected: () => void;
53
+ /** 把选中的标记挪这么多个屏幕点——方向键用的。 */
54
+ nudge: (dx: number, dy: number) => void;
55
+ /**
56
+ * 标注好的图,PNG data URL,按图片自己的尺寸;源图还没解码完时是 null。
57
+ *
58
+ * 重新画一遍,而不是从屏幕上那块画布读回来:那块是按屏幕的分辨率画的,图片显示得比它本身大的时候,
59
+ * 像素比图片本身还多。读回来的话,一张 1408px 的图交出去的是 2200px 的副本。两边用的是同一个
60
+ * `paintAll`,所以出来的就是屏幕上的样子,尺寸和进来时一样。
61
+ */
62
+ render: () => string | null;
63
+ /**
64
+ * 把正在写的那段文字提交掉,如果有的话。返回有没有。
65
+ *
66
+ * 任何要把这幅画变成一张图的操作都得先调它。文字在提交之前住在一个 `<textarea>` 里,提交它的是这个
67
+ * 输入框失去焦点——可工具条故意不拿焦点(`onMouseDown` → `preventDefault`),因为写字时按「粗」必须
68
+ * 是改这段字的大小,而不是结束它。于是光标还在文字里就按「完成」,什么也没提交:字在屏幕上,在画布
69
+ * 上面的一个输入框里,而被裁出来的是画布。出来的图上没有那段字。
70
+ *
71
+ * `true` 表示提交了东西、画布**还没有**重画——重画是一个 effect,要等 React 把状态变化刷出去以后才
72
+ * 落下。调用方等一帧再读。
73
+ */
74
+ flushText: () => boolean;
75
+ /**
76
+ * `flushText` 怎么够到那段文字——它住在 `AnnotateCanvas` 里,不在这里。
77
+ *
78
+ * 用 ref 而不是 state:画布在写字时填上它、写完清空,没有任何东西因为它而渲染得不一样。为了这一次调用
79
+ * 把整套编辑状态提到这里来,要搬好几百行。
80
+ */
81
+ pendingText: React.RefObject<(() => void) | null>;
82
+ shapes: Shape[];
83
+ canvas: React.RefObject<HTMLCanvasElement | null>;
84
+ setHistory: React.Dispatch<React.SetStateAction<History>>;
85
+ image: React.RefObject<Decoded | null>;
86
+ /** 这个网格大小的马赛克取样用的那张平均过的图,用到时才建,建了就缓存。 */
87
+ mosaicSourceFor(block: number): HTMLCanvasElement | null;
88
+ ready: boolean;
89
+ /**
90
+ * 这个标注器先后解码过几张图——「源图换了,重画」唯一诚实的依赖。
91
+ *
92
+ * `ready` 说不出这件事。新图解码时它变成 false,落地后又变回 true,所以两次截图之间它的**值**两次都是
93
+ * true——要是解码快到在 React 把那个 `false` 刷出去之前就完成了,两次更新落在同一轮里,这个标志根本没
94
+ * 变,盯着它的 effect 再也不会跑。`image` 也说不出:它是 ref,所以压根不能当依赖。
95
+ *
96
+ * 截图浮层是这件事看得出代价的地方。它在一个以 `ready` 为依赖的 effect 里用这张位图画冻结的桌面,而
97
+ * 第一次截图还开着时又截了一次,五毫秒就解码完了——于是浮层一边从这一次的图里裁,一边继续显示**上一次**
98
+ * 的画面,也再没发出让窗口上屏的 `ready` 握手,窗口只好等它 1500ms 的兜底超时。
99
+ */
100
+ revision: number;
101
+ /**
102
+ * 这张图和所有从它派生出来的 canvas 用的色彩空间。
103
+ *
104
+ * 传下去而不是各处自己猜:裁剪出来的那张、马赛克取样的那张、放大镜里的那张,都得跟主画布同一
105
+ * 个空间,否则 `drawImage` 会在它们之间做转换——一次转换就是一次色偏,而且只偏彩色不偏灰。
106
+ */
107
+ colorSpace: PredefinedColorSpace;
108
+ /**
109
+ * 源图的原始宽度,放在 state 里而不是从 ref 上读。
110
+ *
111
+ * 这里的每个尺寸——线宽、字号、马赛克格子——都由它推出来,从 `image.current` 推的话,推导就没有诚实的
112
+ * 依赖:ref 当不了依赖,拿 `ready` 标志顶替它是一句谎话,lint 拒绝它是对的。
113
+ */
114
+ width: number;
115
+ /** 现在画一笔马赛克会用的网格。已经画上的标记各自带着自己的。 */
116
+ block: number;
117
+ /**
118
+ * 在图片适配后的尺寸下,一个屏幕点盖住多少个图片像素:工具条给出的尺寸是屏幕尺寸,标记存的是图片像素,
119
+ * 靠它换算。由画布报上来——只有画布知道图片在屏幕上有多大。
120
+ */
121
+ unit: number;
122
+ setUnit: (unit: number) => void;
123
+ }
124
+ export interface AnnotatorOptions {
125
+ /**
126
+ * 一个随图片变化的值,哪怕字节没有变。
127
+ *
128
+ * 截图浮层是一张长期存在的页面——显示、隐藏,而不是建了又拆——所以 `src` 是唯一能告诉它**新的**一次
129
+ * 截图开始了的东西。两次截图之间屏幕没变的话,编码出来的 PNG 逐字节相同,`src` 也就相同,第一次画上
130
+ * 的标记在第二次还会在。
131
+ */
132
+ session?: number | string;
133
+ /**
134
+ * 这张图的数值属于哪个色彩空间。
135
+ *
136
+ * 只有截图会说这件事,因为只有它拿到的是显示器帧缓冲的原始数值——一台 Display P3 的机器上,
137
+ * 屏幕上的纯红在那串数里是 234,51,35。默认按 sRGB 读,那是每一张解码出来的图片的答案(PNG
138
+ * 自带 profile,浏览器已经替我们转好了),也是没有色彩管理时的安全答案。
139
+ */
140
+ colorSpace?: PredefinedColorSpace;
141
+ }
142
+ /**
143
+ * 屏幕的原始像素,直接从主进程来。
144
+ *
145
+ * 已经是 RGBA:从平台的 BGRA 换过来,在主进程里是五毫秒,放到渲染进程里就是一个 22MB 的循环,还是在
146
+ * 要把结果画出来的那个线程上。
147
+ */
148
+ export interface RawPixels {
149
+ pixels: Uint8Array;
150
+ width: number;
151
+ height: number;
152
+ }
153
+ export declare function useAnnotator(src: string | RawPixels | null, options?: AnnotatorOptions): Annotator;
154
+ export {};
@@ -0,0 +1,227 @@
1
+ /**
2
+ * 给一张图做标注背后的算术:一个标记是什么、撤销是什么意思、步骤编号怎么数、马赛克盖住哪些格子,
3
+ * 以及放大时图往哪里去。
4
+ *
5
+ * 和组件分开放,是因为这些都可能错得截图看不出来。缩放不守住锚点,滚轮每走一格就漂一点;步骤编号
6
+ * 存下来而不是数出来,撤掉第三个的那一刻就成了 1、2、4。两者在一张静止的图里都看不见,而它们又都是
7
+ * 有确定答案的普通函数,所以按普通函数来测。
8
+ *
9
+ * 这里的每个坐标都是图片的**原始**像素。画布按这个尺寸打开、由 CSS 缩小显示,所以 3000px 截图上的
10
+ * 标记就按 3000px 存,保存出去时和截图一样清楚。
11
+ */
12
+ /**
13
+ * 每个工具都画点什么。
14
+ *
15
+ * 没有指针工具,这是有意的。选择曾经需要它;后来在所有「放置」而非「涂抹」的工具下,每个标记都能直接
16
+ * 拿起来——这时一个只剩「按在空处而不画」这点本事的工具,不只是多余,还会误导:它暗示碰一个已有的标记
17
+ * 得先切到它,而实际发生的正好相反。
18
+ */
19
+ export type Tool = "pen" | "arrow" | "line" | "rect" | "ellipse" | "step" | "text" | "mosaic";
20
+ export interface Point {
21
+ x: number;
22
+ y: number;
23
+ }
24
+ export interface Shape {
25
+ tool: Tool;
26
+ colour: string;
27
+ /** 画笔和马赛克留下每一个点;其余的由拖动的起点和终点决定。 */
28
+ points: Point[];
29
+ text?: string;
30
+ /**
31
+ * 用原始像素,所以和其他标记一样随图缩放:文字的字号、步骤标号的半径。
32
+ */
33
+ size?: number;
34
+ /** 仅文字:折行的栏宽,原始像素。 */
35
+ width?: number;
36
+ /** 仅文字:这个宽度是手拖出来的,所以改文字时保留它,不重新适配。 */
37
+ fixed?: boolean;
38
+ /** 仅文字:它最后有多高,从打字的那个输入框量出来。 */
39
+ height?: number;
40
+ /** 仅文字:文字后面的 CSS 颜色,undefined 表示没有底。 */
41
+ background?: string;
42
+ /**
43
+ * 这个标记画下时的线宽,原始像素。
44
+ *
45
+ * 记在标记上,而不是画的时候从工具条读,因为工具条管的是**接下来**画什么。按当前设置来画的话,图上
46
+ * 已有的每一笔都会在调粗细的那一刻跟着变——画三个箭头,觉得第四个该细一点,前三个也一起变细。文字一直
47
+ * 出于同一个理由保留自己的 `size`;其余工具才是例外。
48
+ */
49
+ stroke?: number;
50
+ /** 仅马赛克:画它时的格子大小和笔刷宽度。理由同上。 */
51
+ block?: number;
52
+ brush?: number;
53
+ }
54
+ /** 每个点都平移;标记的其他东西不变。 */
55
+ export declare function moveShape(shape: Shape, dx: number, dy: number): Shape;
56
+ /** 文字上调宽度的那个手柄,它不是文字的某个点。 */
57
+ export declare const WIDTH_HANDLE = -1;
58
+ /**
59
+ * 选中标记上的手柄,原始像素。
60
+ *
61
+ * 只放在拖动有意义的地方:直线和箭头的两端、矩形和椭圆的四个角、文字栏的右边缘。画笔的一笔没有能代表
62
+ * 它的点,步骤标号的大小从工具条改,所以这两种只能移动不能拖大小——给一个拖了没反应的手柄,比不给更糟。
63
+ *
64
+ * 矩形有全部四个角,而不只是画它时的那两个。只有两个的话,从左上角画出的矩形只能从左上角和右下角改
65
+ * 大小:手伸向右上角——一半的时候那是离手最近的一个——那里什么也没有。下标 0 和 1 仍是那两个点;2 和 3
66
+ * 是这两个点推出来的另外两个角。
67
+ */
68
+ export declare function handlesOf(shape: Shape): {
69
+ at: Point;
70
+ index: number;
71
+ }[];
72
+ /** 把一个手柄拖到新位置。标记的其余部分不动。 */
73
+ export declare function resizeShape(shape: Shape, index: number, to: Point): Shape;
74
+ /**
75
+ * 按住 Shift 时拖动的终点落在哪:正方形、正圆,或者整角度的直线。
76
+ *
77
+ * 每个绘图程序都这样用这个键,想把线拉直的人第一个想到的也是它。框取两条边里较长的那条作边长;直线
78
+ * 吸到最近的 15°,水平、竖直和对角线都守得住,中间的角度也不至于够不着。
79
+ */
80
+ export declare function constrainEnd(tool: Tool, from: Point, to: Point): Point;
81
+ /**
82
+ * 标记外面的框,只用来画选中状态,别无他用。
83
+ *
84
+ * 故意给得宽:它提示的是选中了什么,不是断言标记盖住了哪些像素,而一个把自己围着的东西切掉一块的框,
85
+ * 看起来就是出错了。
86
+ */
87
+ export declare function shapeBounds(shape: Shape, stroke: number): {
88
+ x: number;
89
+ y: number;
90
+ w: number;
91
+ h: number;
92
+ };
93
+ /**
94
+ * 点下面是哪个标记,没有就是 -1。
95
+ *
96
+ * 从最新的往前找,所以压在最上面的赢——在看图的人眼里,指着的就是它。
97
+ *
98
+ * 轮廓只在轮廓上算命中,不算它围住的面积:一个矩形**围着**某样东西画,就是为了让那样东西还露着,
99
+ * 吞掉本该点给里面的点击,等于把它的用处抵消了。文字和步骤标号是实心的,整个框都算命中。
100
+ */
101
+ export declare function hitShape(shapes: Shape[], at: Point, tolerance: number): number;
102
+ /**
103
+ * 点下面的所有标记,最上面的在前。
104
+ *
105
+ * 一叠互相压着的标记能被逐个点到,靠的就是它:`hitShape` 永远回答最上面的那个,没有这个,压在底下的
106
+ * 永远够不着。
107
+ */
108
+ export declare function hitShapes(shapes: Shape[], at: Point, tolerance: number): number[];
109
+ /**
110
+ * 点是否在标记自己的框里——就是选中时围着它画的那个框。
111
+ *
112
+ * **只**用于已经选中的标记。一样东西一旦带着看得见的框,人伸手拿的就是那个框:在框里按下去,却发现
113
+ * 自己开始画第二个矩形而不是挪第一个,是「只认轮廓」这条规矩里最让人别扭的一点。要人瞄准 2px 的边去
114
+ * 挪一个明明看得见的形状,不是合理的要求。
115
+ *
116
+ * 故意不用于没选中的标记。`hitShape` 的承诺靠这一点才守得住——矩形**围着**某样东西画,是为了让它露着;
117
+ * 要是里面也吞点击,就没法在一个框里再做标注了。
118
+ */
119
+ export declare function insideBounds(shape: Shape, at: Point, stroke: number, tolerance: number): boolean;
120
+ /**
121
+ * 选中时容许偏多远,原始像素。
122
+ *
123
+ * 随线宽放大——一个 12px 宽的标记就按 12px 宽来瞄——又按一个图片像素占多少屏幕像素反过来缩,所以不管图
124
+ * 怎么显示,容差都是固定的几个**屏幕**像素。图片像素里的固定容差,在 25% 下没法瞄,在 800% 下又粘得离谱。
125
+ *
126
+ * `scale` 是图片适配后的尺寸乘以缩放。以前只用缩放,等于假定 100% 时一个图片像素就是一个屏幕像素:
127
+ * 一张 3000px 的截图适配进窗口只显示成三分之一大,它那 7 像素的余量到屏幕上还不到三个点宽。
128
+ */
129
+ export declare function pickTolerance(stroke: number, scale: number): number;
130
+ /**
131
+ * 把文字折成放得进一栏的若干行,保留文字里已有的换行。
132
+ *
133
+ * 画布自己不会折行——`fillText` 一行有多长就画多长,一路画出图的边缘,文字以前就是这样。度量从外面传进来
134
+ * 而不是从某个 context 取,这样规则不靠 DOM 也能测,输入框和画图也能共用同一个函数。
135
+ *
136
+ * 一段自己就比整栏还宽的片段——一个链接、一串长数字——在字与字之间断开,和输入框(`overflow-wrap`)一样;
137
+ * 留成一整段的话,它会冲出栏宽,跑到文字底板外面。
138
+ */
139
+ export declare function wrapText(measure: (line: string) => number, text: string, maxWidth: number): string[];
140
+ /**
141
+ * 撤销和重做:一张步骤清单加一个指向它的游标,而不是两个栈。
142
+ *
143
+ * 两个栈的写法每次操作都要在两边之间挪条目,而且总在同一个地方把边界弄错:有可重做的步骤时提交新的,
144
+ * 得丢掉那些重做——两个栈下这是一件要单独记住的事。这里它直接从数组切片里落出来。
145
+ */
146
+ export interface History {
147
+ steps: Shape[][];
148
+ cursor: number;
149
+ }
150
+ export declare const emptyHistory: () => History;
151
+ export declare const current: (history: History) => Shape[];
152
+ export declare const canUndo: (history: History) => boolean;
153
+ export declare const canRedo: (history: History) => boolean;
154
+ export declare const undo: (history: History) => History;
155
+ export declare const redo: (history: History) => History;
156
+ /**
157
+ * 记下一个新状态,丢掉游标前面的一切。
158
+ *
159
+ * 撤销后再画,就放弃了被撤掉的那一支——另一种做法是一棵树,而树是要用户自己去走的东西。从来没有人想要
160
+ * 截图标注工具给他一棵树。
161
+ */
162
+ export declare function commit(history: History, shapes: Shape[]): History;
163
+ /**
164
+ * 步骤标号显示几:它在所有步骤标号里排第几,画的时候现数。
165
+ *
166
+ * 数出来而不是存下来,所以撤掉四个里的第二个,剩下的会重新编号,而不是留下 1、3、4。标号描述的是顺序,
167
+ * 顺序是这张清单的属性。
168
+ */
169
+ export declare function stepNumber(shapes: Shape[], index: number): number;
170
+ /**
171
+ * 马赛克的格子大小,原始像素——粗到真能把一张脸、一串密钥遮住。
172
+ *
173
+ * `unit` 是图片适配后,一个屏幕像素盖住多少个图片像素,所以不管图片分辨率多少,屏幕上的颗粒都一样大。
174
+ * 以前它是图片宽度的一个比例,这对截图是对的——截图按一个图片像素对一个设备像素显示——对别的都不对:
175
+ * 一张 1408px 的画适配进窗口,出来是 4pt 的小方块,读起来是噪点而不是打码。
176
+ *
177
+ * `scale` 是粗细控件,所以颗粒能调:遮一行字和遮一整个窗口,要的方块明显不一样大。取整像素,因为它取样
178
+ * 的那份平均过的副本每个格子只有一个像素,格子不是整数就会取到两个像素之间。
179
+ */
180
+ export declare function mosaicBlock(unit: number, scale?: number): number;
181
+ /**
182
+ * 马赛克笔刷画多宽,原始像素:屏幕上大约一行字,活儿大时由粗细控件加宽。
183
+ */
184
+ export declare function mosaicBrush(unit: number, scale?: number): number;
185
+ /** 线宽,原始像素。粗细控件直接乘上去。 */
186
+ export declare const strokeAt: (unit: number, weight: number) => number;
187
+ /**
188
+ * 文字的字号,原始像素。
189
+ *
190
+ * 它长得比线慢:线粗 1.8 倍是加粗,字大 1.8 倍就成了标题。这个指数让三档落在 16、22、32 点——最小那档
191
+ * 也高于界面本身的 14 点,低于它,图上的批注就没人读了。
192
+ */
193
+ export declare const typeAt: (unit: number, weight: number) => number;
194
+ /** 步骤标号的半径,原始像素:三档分别是 11、14、19 点。 */
195
+ export declare const badgeAt: (unit: number, weight: number) => number;
196
+ /**
197
+ * 一笔马赛克盖住哪些网格,用每个格子左上角的 `x,y` 表示。
198
+ *
199
+ * 吸到网格上而不是随手画,有两个理由。对齐的方块读起来是有意的打码,跟着光标走的模糊读起来是一块污渍。
200
+ * 而且网格是幂等的:同一处涂两遍盖住的是同样的格子,紧张地来回涂的人不会比一笔画过的人涂得更深。
201
+ */
202
+ export declare function mosaicCells(points: Point[], brush: number, block: number): string[];
203
+ export declare const ZOOM_MIN = 0.25;
204
+ export declare const ZOOM_MAX = 8;
205
+ /** 缩放按钮按一下;滚轮每一格用它的一部分。 */
206
+ export declare const ZOOM_STEP = 1.25;
207
+ export declare const clampZoom: (zoom: number) => number;
208
+ /**
209
+ * 一次滚轮事件缩放多少。
210
+ *
211
+ * 两种手势到这里是同一种事件。触控板捏合是按着 `ctrlKey` 的滚轮,每次几个像素的增量,一秒几十次,所以
212
+ * 那条路要灵敏。Ctrl+滚轮——Windows 和 Linux 上的缩放方式——也是按着 `ctrlKey` 的滚轮,但一格大约 100px,
213
+ * 照捏合的灵敏度算是 e¹:一格 2.7 倍。所以任何一次事件都不许缩放得比按一下按钮还多。捏合的增量离这个
214
+ * 上限远得很,而普通的一格本来就差不多在那儿。
215
+ */
216
+ export declare function wheelZoomFactor(deltaY: number, pinch: boolean): number;
217
+ /**
218
+ * 绕着屏幕上的一点缩放,让那一点下面的东西留在那一点下面。
219
+ *
220
+ * 舞台按 `translate(offset) scale(zoom)` 绕自身中心变换,所以离中心屏幕偏移为 `v` 的点,对应内容偏移
221
+ * `(v - offset) / zoom`。缩放变化时让它不动,就是把这个等式对新的 offset 解出来——这就是「放大你正在看的
222
+ * 东西」和「放大正中间、再去找它」的区别。
223
+ */
224
+ export declare function zoomAt(from: number, to: number, offset: Point, anchor: Point, centre: Point): {
225
+ zoom: number;
226
+ offset: Point;
227
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * 一个标记怎么画到画布上。
3
+ *
4
+ * 从 `Annotator.tsx` 里提出来,是因为截图浮层要的正是同一件事,而它曾经长出过自己的一份——那份只认得八个
5
+ * 工具里的四个。马赛克、文字、步骤标号和直线都在浮层的工具条上,按了都悄无声息地什么也不画,因为背后的
6
+ * 函数根本没有它们的分支。两个画法,总有一个落在后面。
7
+ *
8
+ * 这里的一切都在图片的**原始**像素里、在没有平移过的 context 上进行。画一块裁剪区域的调用方把 context
9
+ * 平移到裁剪区的原点,其余不变——同一个函数就是这样同时服务标注器的整图画布和浮层的选区的。
10
+ */
11
+ import { type Shape } from "./annotate.ts";
12
+ /** 行距和内边距,输入框和画图共用,两边才能分毫不差。 */
13
+ export declare const LINE = 1.35;
14
+ export declare const PAD = 0.28;
15
+ /**
16
+ * 一个字符串,输入框和画布就不会各走各的。
17
+ *
18
+ * 没有下限。以前有一个 `max(12, size)`,用在两种单位上:画布上是图片像素,输入框里是屏幕像素。一段字号
19
+ * 为 15 个图片像素的文字,在按 0.78 显示的图上,打字时是 12 点、画出来是 11.7——提交的那一刻就缩了一下。
20
+ * 现在字号从屏幕来,本来就不会那么小。
21
+ */
22
+ export declare const fontOf: (size: number) => string;
23
+ /**
24
+ * 画一整张标记清单,马赛克也在内。
25
+ *
26
+ * 知道「马赛克和别的画法不一样」的唯一一处——以前每个调用方都得记着这件事,浮层那份没记住,所以它的马赛克
27
+ * 按钮什么也不画。
28
+ */
29
+ export declare function paintAll(ctx: CanvasRenderingContext2D, shapes: Shape[], options: {
30
+ stroke: number;
31
+ /** 按格子大小取,因为来源的分辨率**就是**网格——见 `useAnnotator`。 */
32
+ mosaicSourceFor: (block: number) => HTMLCanvasElement | null;
33
+ block: number;
34
+ brush: number;
35
+ }): void;