@edv4h/usketch-plugin-edge-pan 0.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/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/edge-pan-config.d.ts +63 -0
- package/dist/edge-pan-config.d.ts.map +1 -0
- package/dist/edge-pan-config.js +54 -0
- package/dist/edge-pan-config.js.map +1 -0
- package/dist/edge-pan-runtime.d.ts +8 -0
- package/dist/edge-pan-runtime.d.ts.map +1 -0
- package/dist/edge-pan-runtime.js +138 -0
- package/dist/edge-pan-runtime.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/plugin.d.ts +11 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +43 -0
- package/dist/plugin.js.map +1 -0
- package/dist/register-edge-pan-hud.d.ts +16 -0
- package/dist/register-edge-pan-hud.d.ts.map +1 -0
- package/dist/register-edge-pan-hud.js +59 -0
- package/dist/register-edge-pan-hud.js.map +1 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 EdV4H
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# @edv4h/usketch-plugin-edge-pan
|
|
2
|
+
|
|
3
|
+
Shape をドラッグ中、ポインタを**画角の端**に寄せると、その方向へ**画角が自動でスライド(スクロール)**するプラグイン。tldraw の "edge scrolling" 相当。掴んでいる Shape はカーソル下に留まったまま、世界がその下をスクロールします。
|
|
4
|
+
|
|
5
|
+
## 仕組み
|
|
6
|
+
|
|
7
|
+
- ポインタが画角端の「帯(margin)」に入ると、`BoardStore.panBy` で毎フレーム画角をパンします。速度は**帯の奥ほど速く**(境界で 0・端で最大)。
|
|
8
|
+
- パンで画角が動くと、カーソルが止まっていてもドラッグ中のツールは位置を再計算しません(掴んだ Shape がワールドに取り残される)。そこで本プラグインは、パンした分だけ**ドラッグ中の Shape をワールド空間で直接移動**させ、画面上はカーソル下に留めます(移動量 = 実パン量 ÷ zoom)。
|
|
9
|
+
- ドラッグ中に動いた Shape の id(select ツールが動かした親+子孫)を蓄積し、それらを追従対象にします。移動は transient(コマンド化しない)ので Undo を汚さず、drop 時に select ツールが最終位置を 1 コマンドとして確定します(`commit()` は現在の store 位置を採るため追従分も含まれる)。
|
|
10
|
+
|
|
11
|
+
## 使い方
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { createEdgePanPlugin } from "@edv4h/usketch-plugin-edge-pan";
|
|
15
|
+
|
|
16
|
+
// 既定のまま
|
|
17
|
+
createEdgePanPlugin();
|
|
18
|
+
|
|
19
|
+
// ホスト側で挙動を設定
|
|
20
|
+
createEdgePanPlugin({
|
|
21
|
+
edgeSize: 64, // 反応する端の帯の幅(px)
|
|
22
|
+
maxSpeed: 24, // 帯の最奥での 1 フレームあたりのパン量(px)
|
|
23
|
+
axes: "both", // "both" | "horizontal" | "vertical"
|
|
24
|
+
curve: (d) => d * d, // 端に近づくほど急加速(既定は線形)
|
|
25
|
+
});
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## オプション(すべてホストが設定可能)
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
interface EdgePanOptions {
|
|
32
|
+
enabled?: boolean | (() => boolean); // 既定 true
|
|
33
|
+
edgeSize?: number | (() => number); // 既定 48 (px)
|
|
34
|
+
maxSpeed?: number | (() => number); // 既定 16 (px/frame)
|
|
35
|
+
axes?: EdgePanAxes | (() => EdgePanAxes); // 既定 "both"
|
|
36
|
+
curve?: (depth: number) => number; // 0..1 → 0..1、既定は線形
|
|
37
|
+
}
|
|
38
|
+
type EdgePanAxes = "both" | "horizontal" | "vertical";
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
- **すべて getter(`() => value`)でも渡せます** — 設定 UI のスライダー等からライブに反映されます(値は毎フレーム評価)。
|
|
42
|
+
- 数値は堅牢化のためクランプ・不正値は既定へフォールバック(`edgeSize >= 1`、`maxSpeed >= 0`)。
|
|
43
|
+
|
|
44
|
+
## HUD(end-user 向け)
|
|
45
|
+
|
|
46
|
+
プロジェクト規約どおり独自 UI は持たず、共有 HUD に設定グループ「端で画角スライド」を登録します(有効 / 端の帯(px) / 最大速度 / 対象軸)。**HUD の上書きはホスト options より優先**され、options を土台に end-user がその場で微調整できます。
|
|
47
|
+
|
|
48
|
+
## Snap プラグインとの併用
|
|
49
|
+
|
|
50
|
+
`@edv4h/usketch-plugin-snap` は `store.updateShape` をモンキーパッチして、ドラッグ中の位置更新をガイドへスナップします。自動パンの追従移動は「画角補正」であってユーザー操作ではないため、追従はスナップさせずカーソル下へ留めたい(スナップさせると、ワールド固定のガイドに貼り付いてスクロールしても図形が動かなくなる)。
|
|
51
|
+
|
|
52
|
+
edge-pan は setup 時に掴んだ**パッチ前の生 `updateShape`** で追従を行うことでこれを回避します。そのため **edge-pan を snap より先に登録**してください(先に登録されていれば生の updateShape を掴めます)。スクロール中は追従(非スナップ)、ポインタを実際に動かせば snap は通常どおり再作動します。
|
|
53
|
+
|
|
54
|
+
## 補足
|
|
55
|
+
|
|
56
|
+
- パンは `BoardStore` の viewport constraint を通るので、`setViewportConstraint`(例: dashboard のスクロール制限)が効いている盤面では、その範囲内でのみスライドします。
|
|
57
|
+
- ドラッグ検知は `shape:updated`+pointer-down、終了は `shapes:move-end` / `canvas:pointerup`。
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { Point } from "@edv4h/usketch-shared";
|
|
2
|
+
/** 自動パンを許可する軸。 */
|
|
3
|
+
export type EdgePanAxes = "both" | "horizontal" | "vertical";
|
|
4
|
+
/** `value` そのものか、フレームごとに評価される getter(ライブ変更用)。 */
|
|
5
|
+
export type Live<T> = T | (() => T);
|
|
6
|
+
/**
|
|
7
|
+
* edge-pan プラグインのホスト向けオプション。すべて省略可能で、数値/真偽/enum は
|
|
8
|
+
* getter でも渡せる(設定 UI からの即時反映用に毎フレーム評価される)。
|
|
9
|
+
*/
|
|
10
|
+
export interface EdgePanOptions {
|
|
11
|
+
/** 機能全体の ON/OFF。既定 true。 */
|
|
12
|
+
enabled?: Live<boolean>;
|
|
13
|
+
/** 端の「反応する帯」の幅(px)。この帯にポインタが入ると自動パンが始まる。既定 48。 */
|
|
14
|
+
edgeSize?: Live<number>;
|
|
15
|
+
/** 帯の最奥での 1 フレームあたりのパン量(px)。奥ほど速くなる。既定 16。 */
|
|
16
|
+
maxSpeed?: Live<number>;
|
|
17
|
+
/** 自動パンを許可する軸。既定 "both"。 */
|
|
18
|
+
axes?: Live<EdgePanAxes>;
|
|
19
|
+
/**
|
|
20
|
+
* 帯への食い込み深さ(0..1)を速度係数(0..1)へ写す曲線。既定は線形 `d => d`。
|
|
21
|
+
* 例: `d => d * d`(端に近づくほど急加速)。
|
|
22
|
+
*/
|
|
23
|
+
curve?: (depth: number) => number;
|
|
24
|
+
}
|
|
25
|
+
export declare const EDGE_PAN_DEFAULTS: {
|
|
26
|
+
readonly enabled: true;
|
|
27
|
+
readonly edgeSize: 48;
|
|
28
|
+
readonly maxSpeed: 16;
|
|
29
|
+
readonly axes: EdgePanAxes;
|
|
30
|
+
};
|
|
31
|
+
/** 有限かつ下限以上ならその値、さもなくば fallback。 */
|
|
32
|
+
export declare function clampNumber(value: number, min: number, fallback: number): number;
|
|
33
|
+
/** 実際にパン計算へ渡す、解決済み設定。 */
|
|
34
|
+
export interface ResolvedEdgePan {
|
|
35
|
+
enabled: boolean;
|
|
36
|
+
edgeSize: number;
|
|
37
|
+
maxSpeed: number;
|
|
38
|
+
axes: EdgePanAxes;
|
|
39
|
+
curve: (depth: number) => number;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* ポインタの画面座標(canvas ローカル px)と canvas サイズから、1 フレームのパン量を求める純関数。
|
|
43
|
+
*
|
|
44
|
+
* 方向は「寄せた端の先を見せる」向き:
|
|
45
|
+
* - 左端 → 中身が右へ動く(vp.x を増やす) → dx > 0
|
|
46
|
+
* - 右端 → dx < 0 / 上端 → dy > 0 / 下端 → dy < 0
|
|
47
|
+
* `panBy(dx, dy)` にそのまま渡せる(画面ピクセルオフセット加算)。
|
|
48
|
+
*/
|
|
49
|
+
export declare function edgePanDelta(point: Point, size: {
|
|
50
|
+
width: number;
|
|
51
|
+
height: number;
|
|
52
|
+
}, resolved: ResolvedEdgePan): {
|
|
53
|
+
dx: number;
|
|
54
|
+
dy: number;
|
|
55
|
+
};
|
|
56
|
+
/** options(value|getter)と HUD overrides を重ねて解決するリゾルバを作る。overrides が優先。 */
|
|
57
|
+
export declare function createResolver(options: EdgePanOptions, overrides: Partial<Pick<EdgePanOptions, "enabled" | "edgeSize" | "maxSpeed" | "axes">> & {
|
|
58
|
+
enabled?: boolean;
|
|
59
|
+
edgeSize?: number;
|
|
60
|
+
maxSpeed?: number;
|
|
61
|
+
axes?: EdgePanAxes;
|
|
62
|
+
}): () => ResolvedEdgePan;
|
|
63
|
+
//# sourceMappingURL=edge-pan-config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-pan-config.d.ts","sourceRoot":"","sources":["../src/edge-pan-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,uBAAuB,CAAC;AAEnD,kBAAkB;AAClB,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,YAAY,GAAG,UAAU,CAAC;AAE7D,iDAAiD;AACjD,MAAM,MAAM,IAAI,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;AAEpC;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC9B,4BAA4B;IAC5B,OAAO,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACxB,8CAA8C;IAC9C,QAAQ,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;IACxB,4BAA4B;IAC5B,IAAI,CAAC,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;IACzB;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CAClC;AAED,eAAO,MAAM,iBAAiB;aAC7B,OAAO;aACP,QAAQ,EAAE,EAAE;aACZ,QAAQ,EAAE,EAAE;aACZ,IAAI,EAAY,WAAW;CAClB,CAAC;AAIX,oCAAoC;AACpC,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEhF;AAED,yBAAyB;AACzB,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,WAAW,CAAC;IAClB,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;CACjC;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC3B,KAAK,EAAE,KAAK,EACZ,IAAI,EAAE;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,EACvC,QAAQ,EAAE,eAAe,GACvB;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAoB5B;AAED,0EAA0E;AAC1E,wBAAgB,cAAc,CAC7B,OAAO,EAAE,cAAc,EACvB,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,cAAc,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC,CAAC,GAAG;IACxF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,WAAW,CAAC;CACnB,GACC,MAAM,eAAe,CAoBvB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
export const EDGE_PAN_DEFAULTS = {
|
|
2
|
+
enabled: true,
|
|
3
|
+
edgeSize: 48,
|
|
4
|
+
maxSpeed: 16,
|
|
5
|
+
axes: "both",
|
|
6
|
+
};
|
|
7
|
+
const LINEAR = (d) => d;
|
|
8
|
+
/** 有限かつ下限以上ならその値、さもなくば fallback。 */
|
|
9
|
+
export function clampNumber(value, min, fallback) {
|
|
10
|
+
return Number.isFinite(value) && value >= min ? value : fallback;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* ポインタの画面座標(canvas ローカル px)と canvas サイズから、1 フレームのパン量を求める純関数。
|
|
14
|
+
*
|
|
15
|
+
* 方向は「寄せた端の先を見せる」向き:
|
|
16
|
+
* - 左端 → 中身が右へ動く(vp.x を増やす) → dx > 0
|
|
17
|
+
* - 右端 → dx < 0 / 上端 → dy > 0 / 下端 → dy < 0
|
|
18
|
+
* `panBy(dx, dy)` にそのまま渡せる(画面ピクセルオフセット加算)。
|
|
19
|
+
*/
|
|
20
|
+
export function edgePanDelta(point, size, resolved) {
|
|
21
|
+
const { edgeSize, maxSpeed, axes, curve } = resolved;
|
|
22
|
+
if (edgeSize <= 0 || maxSpeed <= 0)
|
|
23
|
+
return { dx: 0, dy: 0 };
|
|
24
|
+
// 食い込み(px)を 0..1 に正規化し curve を通して速度係数へ。
|
|
25
|
+
const factor = (into) => maxSpeed * curve(Math.min(1, Math.max(0, into / edgeSize)));
|
|
26
|
+
let dx = 0;
|
|
27
|
+
let dy = 0;
|
|
28
|
+
if (axes !== "vertical") {
|
|
29
|
+
if (point.x < edgeSize)
|
|
30
|
+
dx = factor(edgeSize - point.x);
|
|
31
|
+
else if (point.x > size.width - edgeSize)
|
|
32
|
+
dx = -factor(point.x - (size.width - edgeSize));
|
|
33
|
+
}
|
|
34
|
+
if (axes !== "horizontal") {
|
|
35
|
+
if (point.y < edgeSize)
|
|
36
|
+
dy = factor(edgeSize - point.y);
|
|
37
|
+
else if (point.y > size.height - edgeSize)
|
|
38
|
+
dy = -factor(point.y - (size.height - edgeSize));
|
|
39
|
+
}
|
|
40
|
+
return { dx, dy };
|
|
41
|
+
}
|
|
42
|
+
/** options(value|getter)と HUD overrides を重ねて解決するリゾルバを作る。overrides が優先。 */
|
|
43
|
+
export function createResolver(options, overrides) {
|
|
44
|
+
const read = (live) => typeof live === "function" ? live() : live;
|
|
45
|
+
return () => {
|
|
46
|
+
const enabled = overrides.enabled ?? read(options.enabled) ?? EDGE_PAN_DEFAULTS.enabled;
|
|
47
|
+
const edgeSize = clampNumber(overrides.edgeSize ?? read(options.edgeSize) ?? EDGE_PAN_DEFAULTS.edgeSize, 1, EDGE_PAN_DEFAULTS.edgeSize);
|
|
48
|
+
const maxSpeed = clampNumber(overrides.maxSpeed ?? read(options.maxSpeed) ?? EDGE_PAN_DEFAULTS.maxSpeed, 0, EDGE_PAN_DEFAULTS.maxSpeed);
|
|
49
|
+
const axes = overrides.axes ?? read(options.axes) ?? EDGE_PAN_DEFAULTS.axes;
|
|
50
|
+
const curve = options.curve ?? LINEAR;
|
|
51
|
+
return { enabled: enabled === true, edgeSize, maxSpeed, axes, curve };
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=edge-pan-config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-pan-config.js","sourceRoot":"","sources":["../src/edge-pan-config.ts"],"names":[],"mappings":"AA4BA,MAAM,CAAC,MAAM,iBAAiB,GAAG;IAChC,OAAO,EAAE,IAAI;IACb,QAAQ,EAAE,EAAE;IACZ,QAAQ,EAAE,EAAE;IACZ,IAAI,EAAE,MAAqB;CAClB,CAAC;AAEX,MAAM,MAAM,GAAG,CAAC,CAAS,EAAE,EAAE,CAAC,CAAC,CAAC;AAEhC,oCAAoC;AACpC,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,GAAW,EAAE,QAAgB;IACvE,OAAO,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AAClE,CAAC;AAWD;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAC3B,KAAY,EACZ,IAAuC,EACvC,QAAyB;IAEzB,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,QAAQ,CAAC;IACrD,IAAI,QAAQ,IAAI,CAAC,IAAI,QAAQ,IAAI,CAAC;QAAE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC;IAE5D,wCAAwC;IACxC,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,QAAQ,GAAG,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAE7F,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,IAAI,EAAE,GAAG,CAAC,CAAC;IAEX,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;QACzB,IAAI,KAAK,CAAC,CAAC,GAAG,QAAQ;YAAE,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;aACnD,IAAI,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,GAAG,QAAQ;YAAE,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;QAC3B,IAAI,KAAK,CAAC,CAAC,GAAG,QAAQ;YAAE,EAAE,GAAG,MAAM,CAAC,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;aACnD,IAAI,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,GAAG,QAAQ;YAAE,EAAE,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,QAAQ,CAAC,CAAC,CAAC;IAC7F,CAAC;IAED,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC;AACnB,CAAC;AAED,0EAA0E;AAC1E,MAAM,UAAU,cAAc,CAC7B,OAAuB,EACvB,SAKC;IAED,MAAM,IAAI,GAAG,CAAI,IAAyB,EAAiB,EAAE,CAC5D,OAAO,IAAI,KAAK,UAAU,CAAC,CAAC,CAAE,IAAgB,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAEzD,OAAO,GAAG,EAAE;QACX,MAAM,OAAO,GAAG,SAAS,CAAC,OAAO,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,iBAAiB,CAAC,OAAO,CAAC;QACxF,MAAM,QAAQ,GAAG,WAAW,CAC3B,SAAS,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,iBAAiB,CAAC,QAAQ,EAC1E,CAAC,EACD,iBAAiB,CAAC,QAAQ,CAC1B,CAAC;QACF,MAAM,QAAQ,GAAG,WAAW,CAC3B,SAAS,CAAC,QAAQ,IAAI,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,iBAAiB,CAAC,QAAQ,EAC1E,CAAC,EACD,iBAAiB,CAAC,QAAQ,CAC1B,CAAC;QACF,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,IAAI,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,IAAI,CAAC;QAC5E,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC;QACtC,OAAO,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;IACvE,CAAC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PluginContext } from "@edv4h/usketch-shared";
|
|
2
|
+
import { type ResolvedEdgePan } from "./edge-pan-config.js";
|
|
3
|
+
/**
|
|
4
|
+
* edge-pan ランタイムを起動する。`resolve()` は毎フレーム最新設定を返すこと(getter/HUD 反映用)。
|
|
5
|
+
* 返り値は teardown。
|
|
6
|
+
*/
|
|
7
|
+
export declare function setupEdgePan(ctx: PluginContext, resolve: () => ResolvedEdgePan): () => void;
|
|
8
|
+
//# sourceMappingURL=edge-pan-runtime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-pan-runtime.d.ts","sourceRoot":"","sources":["../src/edge-pan-runtime.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAsB,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC/E,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAuC1E;;;GAGG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,eAAe,GAAG,MAAM,IAAI,CAqG3F"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { edgePanDelta } from "./edge-pan-config.js";
|
|
2
|
+
/** rAF ラッパ(SSR/テストでは setTimeout フォールバック)。 */
|
|
3
|
+
function scheduleFrame(fn) {
|
|
4
|
+
if (typeof requestAnimationFrame === "function")
|
|
5
|
+
return requestAnimationFrame(fn);
|
|
6
|
+
return setTimeout(fn, 16);
|
|
7
|
+
}
|
|
8
|
+
function cancelFrame(handle) {
|
|
9
|
+
if (typeof cancelAnimationFrame === "function")
|
|
10
|
+
cancelAnimationFrame(handle);
|
|
11
|
+
else
|
|
12
|
+
clearTimeout(handle);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* canvas コンテナの画面上サイズ。minimap も同じ data-testid を持つので、面積最大の要素を採る。
|
|
16
|
+
* 見つからなければ window にフォールバック。
|
|
17
|
+
*/
|
|
18
|
+
function canvasSize() {
|
|
19
|
+
if (typeof document !== "undefined") {
|
|
20
|
+
const nodes = Array.from(document.querySelectorAll('[data-testid="canvas-container"]'));
|
|
21
|
+
let best = null;
|
|
22
|
+
for (const node of nodes) {
|
|
23
|
+
const rect = node.getBoundingClientRect();
|
|
24
|
+
if (!best || rect.width * rect.height > best.width * best.height) {
|
|
25
|
+
best = { width: rect.width, height: rect.height };
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
if (best && best.width > 0 && best.height > 0)
|
|
29
|
+
return best;
|
|
30
|
+
}
|
|
31
|
+
if (typeof window !== "undefined")
|
|
32
|
+
return { width: window.innerWidth, height: window.innerHeight };
|
|
33
|
+
return { width: 0, height: 0 };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* edge-pan ランタイムを起動する。`resolve()` は毎フレーム最新設定を返すこと(getter/HUD 反映用)。
|
|
37
|
+
* 返り値は teardown。
|
|
38
|
+
*/
|
|
39
|
+
export function setupEdgePan(ctx, resolve) {
|
|
40
|
+
// setup 時点の updateShape を掴む。snap プラグインは updateShape をモンキーパッチして
|
|
41
|
+
// pointerDown 中の位置更新をガイドへスナップするが、自動パンの追従移動は「画角補正」であって
|
|
42
|
+
// ユーザー操作ではないためスナップさせたくない。edge-pan を snap より先に登録しておけば、
|
|
43
|
+
// ここで掴めるのはパッチ前の生 updateShape なので、追従だけこれを使ってスナップを回避する。
|
|
44
|
+
const rawUpdateShape = ctx.store.updateShape.bind(ctx.store);
|
|
45
|
+
let lastPointer = null;
|
|
46
|
+
let pointerDown = false;
|
|
47
|
+
let dragActive = false;
|
|
48
|
+
let loop = null;
|
|
49
|
+
// ドラッグ中に動いている shape の id(select ツールが動かした root+子孫を蓄積)。
|
|
50
|
+
// 自動パン中はカーソルが止まっていてもツールが再計算しないので、これらを直接動かして追従させる。
|
|
51
|
+
const movingIds = new Set();
|
|
52
|
+
// 自分の直接移動由来の shape:updated を弾くガード。
|
|
53
|
+
let applyingFollow = false;
|
|
54
|
+
const frame = () => {
|
|
55
|
+
loop = null;
|
|
56
|
+
if (!dragActive || !lastPointer)
|
|
57
|
+
return;
|
|
58
|
+
const settings = resolve();
|
|
59
|
+
if (settings.enabled) {
|
|
60
|
+
const { dx, dy } = edgePanDelta(lastPointer.screenPoint, canvasSize(), settings);
|
|
61
|
+
if (dx !== 0 || dy !== 0) {
|
|
62
|
+
const before = ctx.store.getViewport();
|
|
63
|
+
ctx.store.panBy(dx, dy);
|
|
64
|
+
const after = ctx.store.getViewport();
|
|
65
|
+
// viewport constraint でクランプされ得るので、実際に動いた分だけ追従させる。
|
|
66
|
+
const appliedDx = after.x - before.x;
|
|
67
|
+
const appliedDy = after.y - before.y;
|
|
68
|
+
if ((appliedDx !== 0 || appliedDy !== 0) && after.zoom > 0 && movingIds.size > 0) {
|
|
69
|
+
// 画面上で掴んだ位置に留めるためのワールド移動量(画面 = worldX*zoom + vp.x が不変)。
|
|
70
|
+
const wdx = -appliedDx / after.zoom;
|
|
71
|
+
const wdy = -appliedDy / after.zoom;
|
|
72
|
+
applyingFollow = true;
|
|
73
|
+
try {
|
|
74
|
+
for (const id of movingIds) {
|
|
75
|
+
const shape = ctx.store.getShape(id);
|
|
76
|
+
// snap をバイパスして生 updateShape で動かす(ガイドに貼り付いて止まるのを防ぐ)。
|
|
77
|
+
if (shape)
|
|
78
|
+
rawUpdateShape(id, { x: shape.x + wdx, y: shape.y + wdy });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
applyingFollow = false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
loop = scheduleFrame(frame);
|
|
88
|
+
};
|
|
89
|
+
const startLoop = () => {
|
|
90
|
+
if (loop === null)
|
|
91
|
+
loop = scheduleFrame(frame);
|
|
92
|
+
};
|
|
93
|
+
const stop = () => {
|
|
94
|
+
dragActive = false;
|
|
95
|
+
pointerDown = false;
|
|
96
|
+
movingIds.clear();
|
|
97
|
+
if (loop !== null) {
|
|
98
|
+
cancelFrame(loop);
|
|
99
|
+
loop = null;
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
const offDown = ctx.events.on("canvas:pointerdown", (event) => {
|
|
103
|
+
pointerDown = true;
|
|
104
|
+
lastPointer = event;
|
|
105
|
+
});
|
|
106
|
+
const offMove = ctx.events.on("canvas:pointermove", (event) => {
|
|
107
|
+
lastPointer = event;
|
|
108
|
+
});
|
|
109
|
+
const offUp = ctx.events.on("canvas:pointerup", () => {
|
|
110
|
+
stop();
|
|
111
|
+
});
|
|
112
|
+
// shape が動いた=ドラッグ中とみなす(select ツールのライブ移動を検知)。
|
|
113
|
+
// 最初の 1 つは「選択中の shape」で開始判定し、以降は動いた id をすべて追従対象に蓄積(子孫も拾う)。
|
|
114
|
+
const offUpdated = ctx.events.on("shape:updated", (payload) => {
|
|
115
|
+
if (applyingFollow || !pointerDown)
|
|
116
|
+
return;
|
|
117
|
+
const id = payload?.after?.id ?? payload?.id;
|
|
118
|
+
if (!id)
|
|
119
|
+
return;
|
|
120
|
+
if (!dragActive) {
|
|
121
|
+
if (!ctx.store.getSelection().has(id))
|
|
122
|
+
return;
|
|
123
|
+
dragActive = true;
|
|
124
|
+
startLoop();
|
|
125
|
+
}
|
|
126
|
+
movingIds.add(id);
|
|
127
|
+
});
|
|
128
|
+
const offMoveEnd = ctx.events.on("shapes:move-end", () => stop());
|
|
129
|
+
return () => {
|
|
130
|
+
offDown();
|
|
131
|
+
offMove();
|
|
132
|
+
offUp();
|
|
133
|
+
offUpdated();
|
|
134
|
+
offMoveEnd();
|
|
135
|
+
stop();
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=edge-pan-runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"edge-pan-runtime.js","sourceRoot":"","sources":["../src/edge-pan-runtime.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAwB,MAAM,sBAAsB,CAAC;AAE1E,6CAA6C;AAC7C,SAAS,aAAa,CAAC,EAAc;IACpC,IAAI,OAAO,qBAAqB,KAAK,UAAU;QAAE,OAAO,qBAAqB,CAAC,EAAE,CAAC,CAAC;IAClF,OAAO,UAAU,CAAC,EAAE,EAAE,EAAE,CAAsB,CAAC;AAChD,CAAC;AACD,SAAS,WAAW,CAAC,MAAc;IAClC,IAAI,OAAO,oBAAoB,KAAK,UAAU;QAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;;QACxE,YAAY,CAAC,MAAM,CAAC,CAAC;AAC3B,CAAC;AAED;;;GAGG;AACH,SAAS,UAAU;IAClB,IAAI,OAAO,QAAQ,KAAK,WAAW,EAAE,CAAC;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,gBAAgB,CAAC,kCAAkC,CAAC,CAAC,CAAC;QACxF,IAAI,IAAI,GAA6C,IAAI,CAAC;QAC1D,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAI,IAAoB,CAAC,qBAAqB,EAAE,CAAC;YAC3D,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC;gBAClE,IAAI,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,EAAE,CAAC;YACnD,CAAC;QACF,CAAC;QACD,IAAI,IAAI,IAAI,IAAI,CAAC,KAAK,GAAG,CAAC,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;IAC5D,CAAC;IACD,IAAI,OAAO,MAAM,KAAK,WAAW;QAChC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,WAAW,EAAE,CAAC;IACjE,OAAO,EAAE,KAAK,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;AAChC,CAAC;AAQD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,GAAkB,EAAE,OAA8B;IAC9E,+DAA+D;IAC/D,sDAAsD;IACtD,sDAAsD;IACtD,sDAAsD;IACtD,MAAM,cAAc,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAE7D,IAAI,WAAW,GAA8B,IAAI,CAAC;IAClD,IAAI,WAAW,GAAG,KAAK,CAAC;IACxB,IAAI,UAAU,GAAG,KAAK,CAAC;IACvB,IAAI,IAAI,GAAkB,IAAI,CAAC;IAC/B,sDAAsD;IACtD,kDAAkD;IAClD,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,mCAAmC;IACnC,IAAI,cAAc,GAAG,KAAK,CAAC;IAE3B,MAAM,KAAK,GAAG,GAAG,EAAE;QAClB,IAAI,GAAG,IAAI,CAAC;QACZ,IAAI,CAAC,UAAU,IAAI,CAAC,WAAW;YAAE,OAAO;QACxC,MAAM,QAAQ,GAAG,OAAO,EAAE,CAAC;QAC3B,IAAI,QAAQ,CAAC,OAAO,EAAE,CAAC;YACtB,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,GAAG,YAAY,CAAC,WAAW,CAAC,WAAW,EAAE,UAAU,EAAE,EAAE,QAAQ,CAAC,CAAC;YACjF,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,CAAC;gBAC1B,MAAM,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;gBACvC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;gBACxB,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC;gBACtC,kDAAkD;gBAClD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;gBACrC,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,CAAC;gBACrC,IAAI,CAAC,SAAS,KAAK,CAAC,IAAI,SAAS,KAAK,CAAC,CAAC,IAAI,KAAK,CAAC,IAAI,GAAG,CAAC,IAAI,SAAS,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;oBAClF,wDAAwD;oBACxD,MAAM,GAAG,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC;oBACpC,MAAM,GAAG,GAAG,CAAC,SAAS,GAAG,KAAK,CAAC,IAAI,CAAC;oBACpC,cAAc,GAAG,IAAI,CAAC;oBACtB,IAAI,CAAC;wBACJ,KAAK,MAAM,EAAE,IAAI,SAAS,EAAE,CAAC;4BAC5B,MAAM,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;4BACrC,oDAAoD;4BACpD,IAAI,KAAK;gCAAE,cAAc,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,GAAG,EAAE,CAAC,CAAC;wBACvE,CAAC;oBACF,CAAC;4BAAS,CAAC;wBACV,cAAc,GAAG,KAAK,CAAC;oBACxB,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QACD,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC;IAEF,MAAM,SAAS,GAAG,GAAG,EAAE;QACtB,IAAI,IAAI,KAAK,IAAI;YAAE,IAAI,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAChD,CAAC,CAAC;IAEF,MAAM,IAAI,GAAG,GAAG,EAAE;QACjB,UAAU,GAAG,KAAK,CAAC;QACnB,WAAW,GAAG,KAAK,CAAC;QACpB,SAAS,CAAC,KAAK,EAAE,CAAC;QAClB,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;YACnB,WAAW,CAAC,IAAI,CAAC,CAAC;YAClB,IAAI,GAAG,IAAI,CAAC;QACb,CAAC;IACF,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAqB,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE;QACjF,WAAW,GAAG,IAAI,CAAC;QACnB,WAAW,GAAG,KAAK,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAqB,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE;QACjF,WAAW,GAAG,KAAK,CAAC;IACrB,CAAC,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAqB,kBAAkB,EAAE,GAAG,EAAE;QACxE,IAAI,EAAE,CAAC;IACR,CAAC,CAAC,CAAC;IAEH,6CAA6C;IAC7C,2DAA2D;IAC3D,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAsB,eAAe,EAAE,CAAC,OAAO,EAAE,EAAE;QAClF,IAAI,cAAc,IAAI,CAAC,WAAW;YAAE,OAAO;QAC3C,MAAM,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,OAAO,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC,EAAE;YAAE,OAAO;QAChB,IAAI,CAAC,UAAU,EAAE,CAAC;YACjB,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO;YAC9C,UAAU,GAAG,IAAI,CAAC;YAClB,SAAS,EAAE,CAAC;QACb,CAAC;QACD,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,MAAM,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,iBAAiB,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC;IAElE,OAAO,GAAG,EAAE;QACX,OAAO,EAAE,CAAC;QACV,OAAO,EAAE,CAAC;QACV,KAAK,EAAE,CAAC;QACR,UAAU,EAAE,CAAC;QACb,UAAU,EAAE,CAAC;QACb,IAAI,EAAE,CAAC;IACR,CAAC,CAAC;AACH,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,WAAW,EACX,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,YAAY,EACZ,KAAK,IAAI,EACT,KAAK,eAAe,GACpB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,WAAW,EACX,iBAAiB,EAGjB,YAAY,GAGZ,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { UsketchPlugin } from "@edv4h/usketch-shared";
|
|
2
|
+
import { type EdgePanOptions } from "./edge-pan-config.js";
|
|
3
|
+
/**
|
|
4
|
+
* Shape をドラッグ中、ポインタを画角の端に寄せると画角がその方向へ自動スライドするプラグイン。
|
|
5
|
+
*
|
|
6
|
+
* すべての挙動(有効/端の帯サイズ/最大速度/対象軸/速度カーブ)はホストが `options` で設定でき、
|
|
7
|
+
* 数値・真偽・enum は getter(`() => value`)でライブ変更も可能。加えて end-user は HUD からも
|
|
8
|
+
* 上書きできる(HUD の上書きが options より優先)。
|
|
9
|
+
*/
|
|
10
|
+
export declare function createEdgePanPlugin(options?: EdgePanOptions): UsketchPlugin;
|
|
11
|
+
//# sourceMappingURL=plugin.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC1E,OAAO,EAAoC,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAO7F;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,cAAmB,GAAG,aAAa,CAkC/E"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { createResolver } from "./edge-pan-config.js";
|
|
2
|
+
import { setupEdgePan } from "./edge-pan-runtime.js";
|
|
3
|
+
import { registerEdgePanHud } from "./register-edge-pan-hud.js";
|
|
4
|
+
/**
|
|
5
|
+
* Shape をドラッグ中、ポインタを画角の端に寄せると画角がその方向へ自動スライドするプラグイン。
|
|
6
|
+
*
|
|
7
|
+
* すべての挙動(有効/端の帯サイズ/最大速度/対象軸/速度カーブ)はホストが `options` で設定でき、
|
|
8
|
+
* 数値・真偽・enum は getter(`() => value`)でライブ変更も可能。加えて end-user は HUD からも
|
|
9
|
+
* 上書きできる(HUD の上書きが options より優先)。
|
|
10
|
+
*/
|
|
11
|
+
export function createEdgePanPlugin(options = {}) {
|
|
12
|
+
const overrides = {};
|
|
13
|
+
const listeners = new Set();
|
|
14
|
+
const notify = () => {
|
|
15
|
+
for (const listener of listeners)
|
|
16
|
+
listener();
|
|
17
|
+
};
|
|
18
|
+
const resolve = createResolver(options, overrides);
|
|
19
|
+
return {
|
|
20
|
+
id: "usketch-plugin-edge-pan",
|
|
21
|
+
name: "端で画角スライド",
|
|
22
|
+
setup(ctx) {
|
|
23
|
+
const teardownRuntime = setupEdgePan(ctx, resolve);
|
|
24
|
+
const teardownHud = registerEdgePanHud(ctx, {
|
|
25
|
+
resolve,
|
|
26
|
+
setOverride: (name, value) => {
|
|
27
|
+
overrides[name] = value;
|
|
28
|
+
notify();
|
|
29
|
+
},
|
|
30
|
+
subscribe: (listener) => {
|
|
31
|
+
listeners.add(listener);
|
|
32
|
+
return () => listeners.delete(listener);
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
return () => {
|
|
36
|
+
teardownRuntime();
|
|
37
|
+
teardownHud();
|
|
38
|
+
listeners.clear();
|
|
39
|
+
};
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sourceRoot":"","sources":["../src/plugin.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAyC,MAAM,sBAAsB,CAAC;AAC7F,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAKhE;;;;;;GAMG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAO,GAAmB,EAAE;IAC/D,MAAM,SAAS,GAAc,EAAE,CAAC;IAChC,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;IACxC,MAAM,MAAM,GAAG,GAAG,EAAE;QACnB,KAAK,MAAM,QAAQ,IAAI,SAAS;YAAE,QAAQ,EAAE,CAAC;IAC9C,CAAC,CAAC;IAEF,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC;IAEnD,OAAO;QACN,EAAE,EAAE,yBAAyB;QAC7B,IAAI,EAAE,UAAU;QAEhB,KAAK,CAAC,GAAkB;YACvB,MAAM,eAAe,GAAG,YAAY,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;YACnD,MAAM,WAAW,GAAG,kBAAkB,CAAC,GAAG,EAAE;gBAC3C,OAAO;gBACP,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;oBAC3B,SAAqC,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC;oBACrD,MAAM,EAAE,CAAC;gBACV,CAAC;gBACD,SAAS,EAAE,CAAC,QAAQ,EAAE,EAAE;oBACvB,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;oBACxB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACzC,CAAC;aACD,CAAC,CAAC;YAEH,OAAO,GAAG,EAAE;gBACX,eAAe,EAAE,CAAC;gBAClB,WAAW,EAAE,CAAC;gBACd,SAAS,CAAC,KAAK,EAAE,CAAC;YACnB,CAAC,CAAC;QACH,CAAC;KACD,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PluginContext } from "@edv4h/usketch-shared";
|
|
2
|
+
import type { ResolvedEdgePan } from "./edge-pan-config.js";
|
|
3
|
+
/** HUD が読み書きするための最小 API(plugin.ts が overrides を土台に提供)。 */
|
|
4
|
+
export interface EdgePanHudApi {
|
|
5
|
+
/** 現在の実効設定(options + overrides)を返す。 */
|
|
6
|
+
resolve: () => ResolvedEdgePan;
|
|
7
|
+
/** end-user による上書き値を設定(options より優先)。 */
|
|
8
|
+
setOverride: (name: "enabled" | "edgeSize" | "maxSpeed" | "axes", value: unknown) => void;
|
|
9
|
+
/** 設定変更の購読。 */
|
|
10
|
+
subscribe: (listener: () => void) => () => void;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* edge-pan の HUD 設定を登録する(独自 UI は作らず、共有 HUD に宣言的に寄せる)。返り値は teardown。
|
|
14
|
+
*/
|
|
15
|
+
export declare function registerEdgePanHud(ctx: PluginContext, api: EdgePanHudApi): () => void;
|
|
16
|
+
//# sourceMappingURL=register-edge-pan-hud.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-edge-pan-hud.d.ts","sourceRoot":"","sources":["../src/register-edge-pan-hud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAe,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEzE,0DAA0D;AAC1D,MAAM,WAAW,aAAa;IAC7B,uCAAuC;IACvC,OAAO,EAAE,MAAM,eAAe,CAAC;IAC/B,yCAAyC;IACzC,WAAW,EAAE,CAAC,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1F,eAAe;IACf,SAAS,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,KAAK,MAAM,IAAI,CAAC;CAChD;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,EAAE,GAAG,EAAE,aAAa,GAAG,MAAM,IAAI,CAqDrF"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* edge-pan の HUD 設定を登録する(独自 UI は作らず、共有 HUD に宣言的に寄せる)。返り値は teardown。
|
|
3
|
+
*/
|
|
4
|
+
export function registerEdgePanHud(ctx, api) {
|
|
5
|
+
return ctx.hud.registerSettings({
|
|
6
|
+
id: "usketch-plugin-edge-pan:settings",
|
|
7
|
+
label: "端で画角スライド",
|
|
8
|
+
order: 15,
|
|
9
|
+
fields: [
|
|
10
|
+
{ name: "enabled", label: "有効", type: "boolean" },
|
|
11
|
+
{ name: "edgeSize", label: "端の帯(px)", type: "number", min: 8, max: 200, step: 4 },
|
|
12
|
+
{ name: "maxSpeed", label: "最大速度(px/frame)", type: "number", min: 1, max: 60, step: 1 },
|
|
13
|
+
{
|
|
14
|
+
name: "axes",
|
|
15
|
+
label: "対象軸",
|
|
16
|
+
type: "enum",
|
|
17
|
+
options: [
|
|
18
|
+
{ value: "both", label: "上下左右" },
|
|
19
|
+
{ value: "horizontal", label: "左右のみ" },
|
|
20
|
+
{ value: "vertical", label: "上下のみ" },
|
|
21
|
+
],
|
|
22
|
+
},
|
|
23
|
+
],
|
|
24
|
+
get(name) {
|
|
25
|
+
const s = api.resolve();
|
|
26
|
+
switch (name) {
|
|
27
|
+
case "enabled":
|
|
28
|
+
return s.enabled;
|
|
29
|
+
case "edgeSize":
|
|
30
|
+
return s.edgeSize;
|
|
31
|
+
case "maxSpeed":
|
|
32
|
+
return s.maxSpeed;
|
|
33
|
+
case "axes":
|
|
34
|
+
return s.axes;
|
|
35
|
+
default:
|
|
36
|
+
return undefined;
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
set(name, value) {
|
|
40
|
+
if (name === "enabled") {
|
|
41
|
+
api.setOverride("enabled", value === true || value === "true");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (name === "axes") {
|
|
45
|
+
if (value === "both" || value === "horizontal" || value === "vertical") {
|
|
46
|
+
api.setOverride("axes", value);
|
|
47
|
+
}
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
if (name === "edgeSize" || name === "maxSpeed") {
|
|
51
|
+
const n = Number(value);
|
|
52
|
+
if (Number.isFinite(n))
|
|
53
|
+
api.setOverride(name, n);
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
subscribe: (listener) => api.subscribe(listener),
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=register-edge-pan-hud.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"register-edge-pan-hud.js","sourceRoot":"","sources":["../src/register-edge-pan-hud.ts"],"names":[],"mappings":"AAaA;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAkB,EAAE,GAAkB;IACxE,OAAO,GAAG,CAAC,GAAG,CAAC,gBAAgB,CAAC;QAC/B,EAAE,EAAE,kCAAkC;QACtC,KAAK,EAAE,UAAU;QACjB,KAAK,EAAE,EAAE;QACT,MAAM,EAAE;YACP,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE;YACjD,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE;YACjF,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,gBAAgB,EAAE,IAAI,EAAE,QAAQ,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE;YACvF;gBACC,IAAI,EAAE,MAAM;gBACZ,KAAK,EAAE,KAAK;gBACZ,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACR,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE;oBAChC,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE;oBACtC,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE;iBACpC;aACD;SACD;QACD,GAAG,CAAC,IAAI;YACP,MAAM,CAAC,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;YACxB,QAAQ,IAAI,EAAE,CAAC;gBACd,KAAK,SAAS;oBACb,OAAO,CAAC,CAAC,OAAO,CAAC;gBAClB,KAAK,UAAU;oBACd,OAAO,CAAC,CAAC,QAAQ,CAAC;gBACnB,KAAK,UAAU;oBACd,OAAO,CAAC,CAAC,QAAQ,CAAC;gBACnB,KAAK,MAAM;oBACV,OAAO,CAAC,CAAC,IAAI,CAAC;gBACf;oBACC,OAAO,SAAS,CAAC;YACnB,CAAC;QACF,CAAC;QACD,GAAG,CAAC,IAAI,EAAE,KAAK;YACd,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACxB,GAAG,CAAC,WAAW,CAAC,SAAS,EAAE,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,MAAM,CAAC,CAAC;gBAC/D,OAAO;YACR,CAAC;YACD,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;gBACrB,IAAI,KAAK,KAAK,MAAM,IAAI,KAAK,KAAK,YAAY,IAAI,KAAK,KAAK,UAAU,EAAE,CAAC;oBACxE,GAAG,CAAC,WAAW,CAAC,MAAM,EAAE,KAAoB,CAAC,CAAC;gBAC/C,CAAC;gBACD,OAAO;YACR,CAAC;YACD,IAAI,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;gBAChD,MAAM,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;gBACxB,IAAI,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;oBAAE,GAAG,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;YAClD,CAAC;QACF,CAAC;QACD,SAAS,EAAE,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC;KAChD,CAAC,CAAC;AACJ,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@edv4h/usketch-plugin-edge-pan",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"types": "./dist/index.d.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": {
|
|
9
|
+
"source": "./src/index.ts",
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@edv4h/usketch-shared": "4.13.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"typescript": "7.0.2",
|
|
19
|
+
"vitest": "4.1.11"
|
|
20
|
+
},
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
"dist",
|
|
26
|
+
"README.md"
|
|
27
|
+
],
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/EdV4H/usketch.git",
|
|
32
|
+
"directory": "plugins/usketch-plugin-edge-pan"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/EdV4H/usketch#readme",
|
|
35
|
+
"bugs": {
|
|
36
|
+
"url": "https://github.com/EdV4H/usketch/issues"
|
|
37
|
+
},
|
|
38
|
+
"scripts": {
|
|
39
|
+
"build": "tsc -p tsconfig.json",
|
|
40
|
+
"typecheck": "tsc --noEmit",
|
|
41
|
+
"test": "vitest run --passWithNoTests",
|
|
42
|
+
"clean": "rm -rf dist"
|
|
43
|
+
}
|
|
44
|
+
}
|