@stroke-stabilizer/vue 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +168 -0
- package/dist/index.cjs +95 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +95 -0
- package/dist/index.js.map +1 -0
- package/dist/useStabilizationLevel.d.ts +47 -0
- package/dist/useStabilizationLevel.d.ts.map +1 -0
- package/dist/useStabilizedPointer.d.ts +56 -0
- package/dist/useStabilizedPointer.d.ts.map +1 -0
- package/package.json +51 -0
package/README.md
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# @stroke-stabilizer/vue
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@stroke-stabilizer/vue)
|
|
4
|
+
|
|
5
|
+
[日本語](./docs/README.ja.md)
|
|
6
|
+
|
|
7
|
+
> This is part of the [stroke-stabilizer](https://github.com/usapopopooon/stroke-stabilizer) monorepo
|
|
8
|
+
|
|
9
|
+
Vue composables for stroke stabilization in digital drawing applications.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @stroke-stabilizer/vue @stroke-stabilizer/core
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
### useStabilizedPointer
|
|
20
|
+
|
|
21
|
+
```vue
|
|
22
|
+
<script setup lang="ts">
|
|
23
|
+
import { useStabilizedPointer } from '@stroke-stabilizer/vue'
|
|
24
|
+
|
|
25
|
+
const { process, reset, pointer } = useStabilizedPointer({
|
|
26
|
+
level: 50,
|
|
27
|
+
onPoint: (point) => {
|
|
28
|
+
draw(point.x, point.y)
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
function handlePointerMove(e: PointerEvent) {
|
|
33
|
+
process({
|
|
34
|
+
x: e.clientX,
|
|
35
|
+
y: e.clientY,
|
|
36
|
+
pressure: e.pressure,
|
|
37
|
+
timestamp: e.timeStamp,
|
|
38
|
+
})
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function handlePointerUp() {
|
|
42
|
+
reset()
|
|
43
|
+
}
|
|
44
|
+
</script>
|
|
45
|
+
|
|
46
|
+
<template>
|
|
47
|
+
<canvas @pointermove="handlePointerMove" @pointerup="handlePointerUp" />
|
|
48
|
+
</template>
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### With rAF Batch Processing
|
|
52
|
+
|
|
53
|
+
For high-frequency input devices, use the underlying `StabilizedPointer`'s batch processing:
|
|
54
|
+
|
|
55
|
+
```vue
|
|
56
|
+
<script setup lang="ts">
|
|
57
|
+
import { useStabilizedPointer } from '@stroke-stabilizer/vue'
|
|
58
|
+
import { onMounted, onUnmounted } from 'vue'
|
|
59
|
+
|
|
60
|
+
const { pointer } = useStabilizedPointer({ level: 50 })
|
|
61
|
+
|
|
62
|
+
onMounted(() => {
|
|
63
|
+
pointer.value.enableBatching({
|
|
64
|
+
onBatch: (points) => drawPoints(points),
|
|
65
|
+
})
|
|
66
|
+
})
|
|
67
|
+
|
|
68
|
+
onUnmounted(() => {
|
|
69
|
+
pointer.value.disableBatching()
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
function handlePointerMove(e: PointerEvent) {
|
|
73
|
+
pointer.value.queue({
|
|
74
|
+
x: e.clientX,
|
|
75
|
+
y: e.clientY,
|
|
76
|
+
pressure: e.pressure,
|
|
77
|
+
timestamp: e.timeStamp,
|
|
78
|
+
})
|
|
79
|
+
}
|
|
80
|
+
</script>
|
|
81
|
+
|
|
82
|
+
<template>
|
|
83
|
+
<canvas @pointermove="handlePointerMove" />
|
|
84
|
+
</template>
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### useStabilizationLevel
|
|
88
|
+
|
|
89
|
+
A composable for managing stabilization level with reactive state.
|
|
90
|
+
|
|
91
|
+
```vue
|
|
92
|
+
<script setup lang="ts">
|
|
93
|
+
import {
|
|
94
|
+
useStabilizationLevel,
|
|
95
|
+
useStabilizedPointer,
|
|
96
|
+
} from '@stroke-stabilizer/vue'
|
|
97
|
+
|
|
98
|
+
const { level, setLevel, isEnabled } = useStabilizationLevel({
|
|
99
|
+
initialLevel: 50,
|
|
100
|
+
onChange: (newLevel) => console.log('Level changed:', newLevel),
|
|
101
|
+
})
|
|
102
|
+
|
|
103
|
+
const { process, reset } = useStabilizedPointer({
|
|
104
|
+
level: level.value,
|
|
105
|
+
})
|
|
106
|
+
</script>
|
|
107
|
+
|
|
108
|
+
<template>
|
|
109
|
+
<div>
|
|
110
|
+
<input
|
|
111
|
+
type="range"
|
|
112
|
+
:min="0"
|
|
113
|
+
:max="100"
|
|
114
|
+
:value="level"
|
|
115
|
+
@input="setLevel(Number(($event.target as HTMLInputElement).value))"
|
|
116
|
+
/>
|
|
117
|
+
<span>{{ level }}%</span>
|
|
118
|
+
<span v-if="isEnabled">Stabilization enabled</span>
|
|
119
|
+
</div>
|
|
120
|
+
</template>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## API
|
|
124
|
+
|
|
125
|
+
### useStabilizedPointer(options?)
|
|
126
|
+
|
|
127
|
+
Creates a stabilized pointer instance.
|
|
128
|
+
|
|
129
|
+
**Options:**
|
|
130
|
+
|
|
131
|
+
- `level` - Stabilization level (0-100). Uses preset when specified
|
|
132
|
+
- `filters` - Custom filter array. Used when level is not specified
|
|
133
|
+
- `onPoint` - Callback when a point is processed
|
|
134
|
+
|
|
135
|
+
**Returns:**
|
|
136
|
+
|
|
137
|
+
- `process(point)` - Process a single point
|
|
138
|
+
- `processAll(points)` - Process multiple points
|
|
139
|
+
- `flushBuffer()` - Flush internal buffer
|
|
140
|
+
- `reset()` - Reset the pointer state
|
|
141
|
+
- `addFilter(filter)` - Add a filter dynamically
|
|
142
|
+
- `removeFilter(type)` - Remove a filter by type
|
|
143
|
+
- `updateFilter(type, params)` - Update filter parameters
|
|
144
|
+
- `pointer` - Computed ref to the StabilizedPointer instance
|
|
145
|
+
- `filterCount` - Computed ref to the number of active filters
|
|
146
|
+
|
|
147
|
+
### useStabilizationLevel(options?)
|
|
148
|
+
|
|
149
|
+
Manages stabilization level state.
|
|
150
|
+
|
|
151
|
+
**Options:**
|
|
152
|
+
|
|
153
|
+
- `initialLevel` - Initial level (default: 0)
|
|
154
|
+
- `min` - Minimum level (default: 0)
|
|
155
|
+
- `max` - Maximum level (default: 100)
|
|
156
|
+
- `onChange` - Callback when level changes
|
|
157
|
+
|
|
158
|
+
**Returns:**
|
|
159
|
+
|
|
160
|
+
- `level` - Computed ref to current level
|
|
161
|
+
- `setLevel(value)` - Set the level
|
|
162
|
+
- `increase(amount?)` - Increase level by amount (default: 10)
|
|
163
|
+
- `decrease(amount?)` - Decrease level by amount (default: 10)
|
|
164
|
+
- `isEnabled` - Computed ref indicating if stabilization is active (level > 0)
|
|
165
|
+
|
|
166
|
+
## License
|
|
167
|
+
|
|
168
|
+
[MIT](../../LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
+
const vue = require("vue");
|
|
4
|
+
const core = require("@stroke-stabilizer/core");
|
|
5
|
+
function useStabilizedPointer(options = {}) {
|
|
6
|
+
const { level, filters, onPoint } = options;
|
|
7
|
+
const pointer = vue.shallowRef(
|
|
8
|
+
level !== void 0 ? core.createStabilizedPointer(level) : (() => {
|
|
9
|
+
const p = new core.StabilizedPointer();
|
|
10
|
+
if (filters) {
|
|
11
|
+
for (const filter of filters) {
|
|
12
|
+
p.addFilter(filter);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return p;
|
|
16
|
+
})()
|
|
17
|
+
);
|
|
18
|
+
const filterCount = vue.ref(pointer.value.length);
|
|
19
|
+
function process(point) {
|
|
20
|
+
const result = pointer.value.process(point);
|
|
21
|
+
if (result && onPoint) {
|
|
22
|
+
onPoint(result);
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
function processAll(points) {
|
|
27
|
+
const results = pointer.value.processAll(points);
|
|
28
|
+
if (onPoint) {
|
|
29
|
+
for (const p of results) {
|
|
30
|
+
onPoint(p);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return results;
|
|
34
|
+
}
|
|
35
|
+
function flushBuffer() {
|
|
36
|
+
return pointer.value.flushBuffer();
|
|
37
|
+
}
|
|
38
|
+
function reset() {
|
|
39
|
+
pointer.value.reset();
|
|
40
|
+
}
|
|
41
|
+
function addFilter(filter) {
|
|
42
|
+
pointer.value.addFilter(filter);
|
|
43
|
+
filterCount.value = pointer.value.length;
|
|
44
|
+
}
|
|
45
|
+
function removeFilter(type) {
|
|
46
|
+
const result = pointer.value.removeFilter(type);
|
|
47
|
+
filterCount.value = pointer.value.length;
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
function updateFilter(type, params) {
|
|
51
|
+
return pointer.value.updateFilter(type, params);
|
|
52
|
+
}
|
|
53
|
+
if (vue.getCurrentInstance()) {
|
|
54
|
+
vue.onUnmounted(() => {
|
|
55
|
+
pointer.value.clear();
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
return {
|
|
59
|
+
process,
|
|
60
|
+
processAll,
|
|
61
|
+
flushBuffer,
|
|
62
|
+
reset,
|
|
63
|
+
addFilter,
|
|
64
|
+
removeFilter,
|
|
65
|
+
updateFilter,
|
|
66
|
+
pointer: vue.computed(() => pointer.value),
|
|
67
|
+
filterCount: vue.computed(() => filterCount.value)
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
function useStabilizationLevel(options = {}) {
|
|
71
|
+
const { initialLevel = 0, min = 0, max = 100, onChange } = options;
|
|
72
|
+
const level = vue.ref(Math.max(min, Math.min(max, initialLevel)));
|
|
73
|
+
vue.watch(level, (newValue) => {
|
|
74
|
+
onChange == null ? void 0 : onChange(newValue);
|
|
75
|
+
});
|
|
76
|
+
function setLevel(value) {
|
|
77
|
+
level.value = Math.max(min, Math.min(max, value));
|
|
78
|
+
}
|
|
79
|
+
function increase(amount = 10) {
|
|
80
|
+
setLevel(level.value + amount);
|
|
81
|
+
}
|
|
82
|
+
function decrease(amount = 10) {
|
|
83
|
+
setLevel(level.value - amount);
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
level: vue.computed(() => level.value),
|
|
87
|
+
setLevel,
|
|
88
|
+
increase,
|
|
89
|
+
decrease,
|
|
90
|
+
isEnabled: vue.computed(() => level.value > 0)
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
exports.useStabilizationLevel = useStabilizationLevel;
|
|
94
|
+
exports.useStabilizedPointer = useStabilizedPointer;
|
|
95
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../src/useStabilizedPointer.ts","../src/useStabilizationLevel.ts"],"sourcesContent":["import { ref, shallowRef, computed, onUnmounted, getCurrentInstance } from 'vue'\nimport {\n StabilizedPointer,\n createStabilizedPointer,\n type PointerPoint,\n type Filter,\n} from '@stroke-stabilizer/core'\n\nexport interface UseStabilizedPointerOptions {\n /** Stabilization level (0-100). Uses preset when specified */\n level?: number\n /** Custom filters. Used when level is not specified */\n filters?: Filter[]\n /** Callback when a point is processed */\n onPoint?: (point: PointerPoint) => void\n}\n\n/**\n * Vue Composable for stroke stabilization\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useStabilizedPointer } from '@stroke-stabilizer/vue'\n *\n * const { process, reset } = useStabilizedPointer({\n * level: 50,\n * onPoint: (point) => {\n * // Draw with stabilized point\n * drawPoint(point)\n * }\n * })\n *\n * function handlePointerMove(e: PointerEvent) {\n * process({\n * x: e.clientX,\n * y: e.clientY,\n * pressure: e.pressure,\n * timestamp: e.timeStamp\n * })\n * }\n *\n * function handlePointerUp() {\n * reset()\n * }\n * </script>\n *\n * <template>\n * <canvas @pointermove=\"handlePointerMove\" @pointerup=\"handlePointerUp\" />\n * </template>\n * ```\n */\nexport function useStabilizedPointer(\n options: UseStabilizedPointerOptions = {}\n) {\n const { level, filters, onPoint } = options\n\n // StabilizedPointer instance (non-reactive)\n const pointer = shallowRef<StabilizedPointer>(\n level !== undefined\n ? createStabilizedPointer(level)\n : (() => {\n const p = new StabilizedPointer()\n if (filters) {\n for (const filter of filters) {\n p.addFilter(filter)\n }\n }\n return p\n })()\n )\n\n // Filter count (reactive)\n const filterCount = ref(pointer.value.length)\n\n function process(point: PointerPoint): PointerPoint | null {\n const result = pointer.value.process(point)\n if (result && onPoint) {\n onPoint(result)\n }\n return result\n }\n\n function processAll(points: PointerPoint[]): PointerPoint[] {\n const results = pointer.value.processAll(points)\n if (onPoint) {\n for (const p of results) {\n onPoint(p)\n }\n }\n return results\n }\n\n function flushBuffer(): PointerPoint[] {\n return pointer.value.flushBuffer()\n }\n\n function reset(): void {\n pointer.value.reset()\n }\n\n function addFilter(filter: Filter): void {\n pointer.value.addFilter(filter)\n filterCount.value = pointer.value.length\n }\n\n function removeFilter(type: string): boolean {\n const result = pointer.value.removeFilter(type)\n filterCount.value = pointer.value.length\n return result\n }\n\n function updateFilter<T>(type: string, params: Partial<T>): boolean {\n return pointer.value.updateFilter(type, params)\n }\n\n // Cleanup (only register if in component context)\n if (getCurrentInstance()) {\n onUnmounted(() => {\n pointer.value.clear()\n })\n }\n\n return {\n process,\n processAll,\n flushBuffer,\n reset,\n addFilter,\n removeFilter,\n updateFilter,\n pointer: computed(() => pointer.value),\n filterCount: computed(() => filterCount.value),\n }\n}\n","import { ref, computed, watch } from 'vue'\n\nexport interface UseStabilizationLevelOptions {\n /** Initial value (default: 0) */\n initialLevel?: number\n /** Minimum value (default: 0) */\n min?: number\n /** Maximum value (default: 100) */\n max?: number\n /** Callback when level changes */\n onChange?: (level: number) => void\n}\n\n/**\n * Vue Composable for managing stabilization level\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useStabilizationLevel } from '@stroke-stabilizer/vue'\n *\n * const { level, setLevel, isEnabled } = useStabilizationLevel({\n * initialLevel: 50,\n * onChange: (newLevel) => console.log('Level changed:', newLevel)\n * })\n * </script>\n *\n * <template>\n * <div>\n * <input\n * type=\"range\"\n * :min=\"0\"\n * :max=\"100\"\n * :value=\"level\"\n * @input=\"setLevel(Number(($event.target as HTMLInputElement).value))\"\n * />\n * <span>{{ level }}%</span>\n * <span v-if=\"isEnabled\">Stabilization enabled</span>\n * </div>\n * </template>\n * ```\n */\nexport function useStabilizationLevel(\n options: UseStabilizationLevelOptions = {}\n) {\n const { initialLevel = 0, min = 0, max = 100, onChange } = options\n\n const level = ref(Math.max(min, Math.min(max, initialLevel)))\n\n // Watch for changes\n watch(level, (newValue) => {\n onChange?.(newValue)\n })\n\n function setLevel(value: number): void {\n level.value = Math.max(min, Math.min(max, value))\n }\n\n function increase(amount = 10): void {\n setLevel(level.value + amount)\n }\n\n function decrease(amount = 10): void {\n setLevel(level.value - amount)\n }\n\n return {\n level: computed(() => level.value),\n setLevel,\n increase,\n decrease,\n isEnabled: computed(() => level.value > 0),\n }\n}\n"],"names":["shallowRef","createStabilizedPointer","StabilizedPointer","ref","getCurrentInstance","onUnmounted","computed","watch"],"mappings":";;;;AAoDO,SAAS,qBACd,UAAuC,IACvC;AACA,QAAM,EAAE,OAAO,SAAS,QAAA,IAAY;AAGpC,QAAM,UAAUA,IAAAA;AAAAA,IACd,UAAU,SACNC,KAAAA,wBAAwB,KAAK,KAC5B,MAAM;AACL,YAAM,IAAI,IAAIC,uBAAA;AACd,UAAI,SAAS;AACX,mBAAW,UAAU,SAAS;AAC5B,YAAE,UAAU,MAAM;AAAA,QACpB;AAAA,MACF;AACA,aAAO;AAAA,IACT,GAAA;AAAA,EAAG;AAIT,QAAM,cAAcC,IAAAA,IAAI,QAAQ,MAAM,MAAM;AAE5C,WAAS,QAAQ,OAA0C;AACzD,UAAM,SAAS,QAAQ,MAAM,QAAQ,KAAK;AAC1C,QAAI,UAAU,SAAS;AACrB,cAAQ,MAAM;AAAA,IAChB;AACA,WAAO;AAAA,EACT;AAEA,WAAS,WAAW,QAAwC;AAC1D,UAAM,UAAU,QAAQ,MAAM,WAAW,MAAM;AAC/C,QAAI,SAAS;AACX,iBAAW,KAAK,SAAS;AACvB,gBAAQ,CAAC;AAAA,MACX;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,WAAS,cAA8B;AACrC,WAAO,QAAQ,MAAM,YAAA;AAAA,EACvB;AAEA,WAAS,QAAc;AACrB,YAAQ,MAAM,MAAA;AAAA,EAChB;AAEA,WAAS,UAAU,QAAsB;AACvC,YAAQ,MAAM,UAAU,MAAM;AAC9B,gBAAY,QAAQ,QAAQ,MAAM;AAAA,EACpC;AAEA,WAAS,aAAa,MAAuB;AAC3C,UAAM,SAAS,QAAQ,MAAM,aAAa,IAAI;AAC9C,gBAAY,QAAQ,QAAQ,MAAM;AAClC,WAAO;AAAA,EACT;AAEA,WAAS,aAAgB,MAAc,QAA6B;AAClE,WAAO,QAAQ,MAAM,aAAa,MAAM,MAAM;AAAA,EAChD;AAGA,MAAIC,IAAAA,sBAAsB;AACxBC,QAAAA,YAAY,MAAM;AAChB,cAAQ,MAAM,MAAA;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAASC,IAAAA,SAAS,MAAM,QAAQ,KAAK;AAAA,IACrC,aAAaA,IAAAA,SAAS,MAAM,YAAY,KAAK;AAAA,EAAA;AAEjD;AC5FO,SAAS,sBACd,UAAwC,IACxC;AACA,QAAM,EAAE,eAAe,GAAG,MAAM,GAAG,MAAM,KAAK,aAAa;AAE3D,QAAM,QAAQH,IAAAA,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,YAAY,CAAC,CAAC;AAG5DI,YAAM,OAAO,CAAC,aAAa;AACzB,yCAAW;AAAA,EACb,CAAC;AAED,WAAS,SAAS,OAAqB;AACrC,UAAM,QAAQ,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAAA,EAClD;AAEA,WAAS,SAAS,SAAS,IAAU;AACnC,aAAS,MAAM,QAAQ,MAAM;AAAA,EAC/B;AAEA,WAAS,SAAS,SAAS,IAAU;AACnC,aAAS,MAAM,QAAQ,MAAM;AAAA,EAC/B;AAEA,SAAO;AAAA,IACL,OAAOD,IAAAA,SAAS,MAAM,MAAM,KAAK;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAWA,IAAAA,SAAS,MAAM,MAAM,QAAQ,CAAC;AAAA,EAAA;AAE7C;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { useStabilizedPointer } from './useStabilizedPointer';
|
|
2
|
+
export type { UseStabilizedPointerOptions } from './useStabilizedPointer';
|
|
3
|
+
export { useStabilizationLevel } from './useStabilizationLevel';
|
|
4
|
+
export type { UseStabilizationLevelOptions } from './useStabilizationLevel';
|
|
5
|
+
export type { Point, PointerPoint, Filter } from '@stroke-stabilizer/core';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,wBAAwB,CAAA;AAC7D,YAAY,EAAE,2BAA2B,EAAE,MAAM,wBAAwB,CAAA;AAEzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,YAAY,EAAE,4BAA4B,EAAE,MAAM,yBAAyB,CAAA;AAG3E,YAAY,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { shallowRef, ref, getCurrentInstance, onUnmounted, computed, watch } from "vue";
|
|
2
|
+
import { createStabilizedPointer, StabilizedPointer } from "@stroke-stabilizer/core";
|
|
3
|
+
function useStabilizedPointer(options = {}) {
|
|
4
|
+
const { level, filters, onPoint } = options;
|
|
5
|
+
const pointer = shallowRef(
|
|
6
|
+
level !== void 0 ? createStabilizedPointer(level) : (() => {
|
|
7
|
+
const p = new StabilizedPointer();
|
|
8
|
+
if (filters) {
|
|
9
|
+
for (const filter of filters) {
|
|
10
|
+
p.addFilter(filter);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return p;
|
|
14
|
+
})()
|
|
15
|
+
);
|
|
16
|
+
const filterCount = ref(pointer.value.length);
|
|
17
|
+
function process(point) {
|
|
18
|
+
const result = pointer.value.process(point);
|
|
19
|
+
if (result && onPoint) {
|
|
20
|
+
onPoint(result);
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
function processAll(points) {
|
|
25
|
+
const results = pointer.value.processAll(points);
|
|
26
|
+
if (onPoint) {
|
|
27
|
+
for (const p of results) {
|
|
28
|
+
onPoint(p);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return results;
|
|
32
|
+
}
|
|
33
|
+
function flushBuffer() {
|
|
34
|
+
return pointer.value.flushBuffer();
|
|
35
|
+
}
|
|
36
|
+
function reset() {
|
|
37
|
+
pointer.value.reset();
|
|
38
|
+
}
|
|
39
|
+
function addFilter(filter) {
|
|
40
|
+
pointer.value.addFilter(filter);
|
|
41
|
+
filterCount.value = pointer.value.length;
|
|
42
|
+
}
|
|
43
|
+
function removeFilter(type) {
|
|
44
|
+
const result = pointer.value.removeFilter(type);
|
|
45
|
+
filterCount.value = pointer.value.length;
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function updateFilter(type, params) {
|
|
49
|
+
return pointer.value.updateFilter(type, params);
|
|
50
|
+
}
|
|
51
|
+
if (getCurrentInstance()) {
|
|
52
|
+
onUnmounted(() => {
|
|
53
|
+
pointer.value.clear();
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
process,
|
|
58
|
+
processAll,
|
|
59
|
+
flushBuffer,
|
|
60
|
+
reset,
|
|
61
|
+
addFilter,
|
|
62
|
+
removeFilter,
|
|
63
|
+
updateFilter,
|
|
64
|
+
pointer: computed(() => pointer.value),
|
|
65
|
+
filterCount: computed(() => filterCount.value)
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function useStabilizationLevel(options = {}) {
|
|
69
|
+
const { initialLevel = 0, min = 0, max = 100, onChange } = options;
|
|
70
|
+
const level = ref(Math.max(min, Math.min(max, initialLevel)));
|
|
71
|
+
watch(level, (newValue) => {
|
|
72
|
+
onChange == null ? void 0 : onChange(newValue);
|
|
73
|
+
});
|
|
74
|
+
function setLevel(value) {
|
|
75
|
+
level.value = Math.max(min, Math.min(max, value));
|
|
76
|
+
}
|
|
77
|
+
function increase(amount = 10) {
|
|
78
|
+
setLevel(level.value + amount);
|
|
79
|
+
}
|
|
80
|
+
function decrease(amount = 10) {
|
|
81
|
+
setLevel(level.value - amount);
|
|
82
|
+
}
|
|
83
|
+
return {
|
|
84
|
+
level: computed(() => level.value),
|
|
85
|
+
setLevel,
|
|
86
|
+
increase,
|
|
87
|
+
decrease,
|
|
88
|
+
isEnabled: computed(() => level.value > 0)
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export {
|
|
92
|
+
useStabilizationLevel,
|
|
93
|
+
useStabilizedPointer
|
|
94
|
+
};
|
|
95
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../src/useStabilizedPointer.ts","../src/useStabilizationLevel.ts"],"sourcesContent":["import { ref, shallowRef, computed, onUnmounted, getCurrentInstance } from 'vue'\nimport {\n StabilizedPointer,\n createStabilizedPointer,\n type PointerPoint,\n type Filter,\n} from '@stroke-stabilizer/core'\n\nexport interface UseStabilizedPointerOptions {\n /** Stabilization level (0-100). Uses preset when specified */\n level?: number\n /** Custom filters. Used when level is not specified */\n filters?: Filter[]\n /** Callback when a point is processed */\n onPoint?: (point: PointerPoint) => void\n}\n\n/**\n * Vue Composable for stroke stabilization\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useStabilizedPointer } from '@stroke-stabilizer/vue'\n *\n * const { process, reset } = useStabilizedPointer({\n * level: 50,\n * onPoint: (point) => {\n * // Draw with stabilized point\n * drawPoint(point)\n * }\n * })\n *\n * function handlePointerMove(e: PointerEvent) {\n * process({\n * x: e.clientX,\n * y: e.clientY,\n * pressure: e.pressure,\n * timestamp: e.timeStamp\n * })\n * }\n *\n * function handlePointerUp() {\n * reset()\n * }\n * </script>\n *\n * <template>\n * <canvas @pointermove=\"handlePointerMove\" @pointerup=\"handlePointerUp\" />\n * </template>\n * ```\n */\nexport function useStabilizedPointer(\n options: UseStabilizedPointerOptions = {}\n) {\n const { level, filters, onPoint } = options\n\n // StabilizedPointer instance (non-reactive)\n const pointer = shallowRef<StabilizedPointer>(\n level !== undefined\n ? createStabilizedPointer(level)\n : (() => {\n const p = new StabilizedPointer()\n if (filters) {\n for (const filter of filters) {\n p.addFilter(filter)\n }\n }\n return p\n })()\n )\n\n // Filter count (reactive)\n const filterCount = ref(pointer.value.length)\n\n function process(point: PointerPoint): PointerPoint | null {\n const result = pointer.value.process(point)\n if (result && onPoint) {\n onPoint(result)\n }\n return result\n }\n\n function processAll(points: PointerPoint[]): PointerPoint[] {\n const results = pointer.value.processAll(points)\n if (onPoint) {\n for (const p of results) {\n onPoint(p)\n }\n }\n return results\n }\n\n function flushBuffer(): PointerPoint[] {\n return pointer.value.flushBuffer()\n }\n\n function reset(): void {\n pointer.value.reset()\n }\n\n function addFilter(filter: Filter): void {\n pointer.value.addFilter(filter)\n filterCount.value = pointer.value.length\n }\n\n function removeFilter(type: string): boolean {\n const result = pointer.value.removeFilter(type)\n filterCount.value = pointer.value.length\n return result\n }\n\n function updateFilter<T>(type: string, params: Partial<T>): boolean {\n return pointer.value.updateFilter(type, params)\n }\n\n // Cleanup (only register if in component context)\n if (getCurrentInstance()) {\n onUnmounted(() => {\n pointer.value.clear()\n })\n }\n\n return {\n process,\n processAll,\n flushBuffer,\n reset,\n addFilter,\n removeFilter,\n updateFilter,\n pointer: computed(() => pointer.value),\n filterCount: computed(() => filterCount.value),\n }\n}\n","import { ref, computed, watch } from 'vue'\n\nexport interface UseStabilizationLevelOptions {\n /** Initial value (default: 0) */\n initialLevel?: number\n /** Minimum value (default: 0) */\n min?: number\n /** Maximum value (default: 100) */\n max?: number\n /** Callback when level changes */\n onChange?: (level: number) => void\n}\n\n/**\n * Vue Composable for managing stabilization level\n *\n * @example\n * ```vue\n * <script setup lang=\"ts\">\n * import { useStabilizationLevel } from '@stroke-stabilizer/vue'\n *\n * const { level, setLevel, isEnabled } = useStabilizationLevel({\n * initialLevel: 50,\n * onChange: (newLevel) => console.log('Level changed:', newLevel)\n * })\n * </script>\n *\n * <template>\n * <div>\n * <input\n * type=\"range\"\n * :min=\"0\"\n * :max=\"100\"\n * :value=\"level\"\n * @input=\"setLevel(Number(($event.target as HTMLInputElement).value))\"\n * />\n * <span>{{ level }}%</span>\n * <span v-if=\"isEnabled\">Stabilization enabled</span>\n * </div>\n * </template>\n * ```\n */\nexport function useStabilizationLevel(\n options: UseStabilizationLevelOptions = {}\n) {\n const { initialLevel = 0, min = 0, max = 100, onChange } = options\n\n const level = ref(Math.max(min, Math.min(max, initialLevel)))\n\n // Watch for changes\n watch(level, (newValue) => {\n onChange?.(newValue)\n })\n\n function setLevel(value: number): void {\n level.value = Math.max(min, Math.min(max, value))\n }\n\n function increase(amount = 10): void {\n setLevel(level.value + amount)\n }\n\n function decrease(amount = 10): void {\n setLevel(level.value - amount)\n }\n\n return {\n level: computed(() => level.value),\n setLevel,\n increase,\n decrease,\n isEnabled: computed(() => level.value > 0),\n }\n}\n"],"names":[],"mappings":";;AAoDO,SAAS,qBACd,UAAuC,IACvC;AACA,QAAM,EAAE,OAAO,SAAS,QAAA,IAAY;AAGpC,QAAM,UAAU;AAAA,IACd,UAAU,SACN,wBAAwB,KAAK,KAC5B,MAAM;AACL,YAAM,IAAI,IAAI,kBAAA;AACd,UAAI,SAAS;AACX,mBAAW,UAAU,SAAS;AAC5B,YAAE,UAAU,MAAM;AAAA,QACpB;AAAA,MACF;AACA,aAAO;AAAA,IACT,GAAA;AAAA,EAAG;AAIT,QAAM,cAAc,IAAI,QAAQ,MAAM,MAAM;AAE5C,WAAS,QAAQ,OAA0C;AACzD,UAAM,SAAS,QAAQ,MAAM,QAAQ,KAAK;AAC1C,QAAI,UAAU,SAAS;AACrB,cAAQ,MAAM;AAAA,IAChB;AACA,WAAO;AAAA,EACT;AAEA,WAAS,WAAW,QAAwC;AAC1D,UAAM,UAAU,QAAQ,MAAM,WAAW,MAAM;AAC/C,QAAI,SAAS;AACX,iBAAW,KAAK,SAAS;AACvB,gBAAQ,CAAC;AAAA,MACX;AAAA,IACF;AACA,WAAO;AAAA,EACT;AAEA,WAAS,cAA8B;AACrC,WAAO,QAAQ,MAAM,YAAA;AAAA,EACvB;AAEA,WAAS,QAAc;AACrB,YAAQ,MAAM,MAAA;AAAA,EAChB;AAEA,WAAS,UAAU,QAAsB;AACvC,YAAQ,MAAM,UAAU,MAAM;AAC9B,gBAAY,QAAQ,QAAQ,MAAM;AAAA,EACpC;AAEA,WAAS,aAAa,MAAuB;AAC3C,UAAM,SAAS,QAAQ,MAAM,aAAa,IAAI;AAC9C,gBAAY,QAAQ,QAAQ,MAAM;AAClC,WAAO;AAAA,EACT;AAEA,WAAS,aAAgB,MAAc,QAA6B;AAClE,WAAO,QAAQ,MAAM,aAAa,MAAM,MAAM;AAAA,EAChD;AAGA,MAAI,sBAAsB;AACxB,gBAAY,MAAM;AAChB,cAAQ,MAAM,MAAA;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,SAAS,SAAS,MAAM,QAAQ,KAAK;AAAA,IACrC,aAAa,SAAS,MAAM,YAAY,KAAK;AAAA,EAAA;AAEjD;AC5FO,SAAS,sBACd,UAAwC,IACxC;AACA,QAAM,EAAE,eAAe,GAAG,MAAM,GAAG,MAAM,KAAK,aAAa;AAE3D,QAAM,QAAQ,IAAI,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,YAAY,CAAC,CAAC;AAG5D,QAAM,OAAO,CAAC,aAAa;AACzB,yCAAW;AAAA,EACb,CAAC;AAED,WAAS,SAAS,OAAqB;AACrC,UAAM,QAAQ,KAAK,IAAI,KAAK,KAAK,IAAI,KAAK,KAAK,CAAC;AAAA,EAClD;AAEA,WAAS,SAAS,SAAS,IAAU;AACnC,aAAS,MAAM,QAAQ,MAAM;AAAA,EAC/B;AAEA,WAAS,SAAS,SAAS,IAAU;AACnC,aAAS,MAAM,QAAQ,MAAM;AAAA,EAC/B;AAEA,SAAO;AAAA,IACL,OAAO,SAAS,MAAM,MAAM,KAAK;AAAA,IACjC;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW,SAAS,MAAM,MAAM,QAAQ,CAAC;AAAA,EAAA;AAE7C;"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export interface UseStabilizationLevelOptions {
|
|
2
|
+
/** Initial value (default: 0) */
|
|
3
|
+
initialLevel?: number;
|
|
4
|
+
/** Minimum value (default: 0) */
|
|
5
|
+
min?: number;
|
|
6
|
+
/** Maximum value (default: 100) */
|
|
7
|
+
max?: number;
|
|
8
|
+
/** Callback when level changes */
|
|
9
|
+
onChange?: (level: number) => void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Vue Composable for managing stabilization level
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```vue
|
|
16
|
+
* <script setup lang="ts">
|
|
17
|
+
* import { useStabilizationLevel } from '@stroke-stabilizer/vue'
|
|
18
|
+
*
|
|
19
|
+
* const { level, setLevel, isEnabled } = useStabilizationLevel({
|
|
20
|
+
* initialLevel: 50,
|
|
21
|
+
* onChange: (newLevel) => console.log('Level changed:', newLevel)
|
|
22
|
+
* })
|
|
23
|
+
* </script>
|
|
24
|
+
*
|
|
25
|
+
* <template>
|
|
26
|
+
* <div>
|
|
27
|
+
* <input
|
|
28
|
+
* type="range"
|
|
29
|
+
* :min="0"
|
|
30
|
+
* :max="100"
|
|
31
|
+
* :value="level"
|
|
32
|
+
* @input="setLevel(Number(($event.target as HTMLInputElement).value))"
|
|
33
|
+
* />
|
|
34
|
+
* <span>{{ level }}%</span>
|
|
35
|
+
* <span v-if="isEnabled">Stabilization enabled</span>
|
|
36
|
+
* </div>
|
|
37
|
+
* </template>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function useStabilizationLevel(options?: UseStabilizationLevelOptions): {
|
|
41
|
+
level: import("vue").ComputedRef<number>;
|
|
42
|
+
setLevel: (value: number) => void;
|
|
43
|
+
increase: (amount?: number) => void;
|
|
44
|
+
decrease: (amount?: number) => void;
|
|
45
|
+
isEnabled: import("vue").ComputedRef<boolean>;
|
|
46
|
+
};
|
|
47
|
+
//# sourceMappingURL=useStabilizationLevel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStabilizationLevel.d.ts","sourceRoot":"","sources":["../src/useStabilizationLevel.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,4BAA4B;IAC3C,iCAAiC;IACjC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,mCAAmC;IACnC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,kCAAkC;IAClC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CACnC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,GAAE,4BAAiC;;sBAWjB,MAAM,KAAG,IAAI;mCAIN,IAAI;mCAIJ,IAAI;;EAWrC"}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { StabilizedPointer, type PointerPoint, type Filter } from '@stroke-stabilizer/core';
|
|
2
|
+
export interface UseStabilizedPointerOptions {
|
|
3
|
+
/** Stabilization level (0-100). Uses preset when specified */
|
|
4
|
+
level?: number;
|
|
5
|
+
/** Custom filters. Used when level is not specified */
|
|
6
|
+
filters?: Filter[];
|
|
7
|
+
/** Callback when a point is processed */
|
|
8
|
+
onPoint?: (point: PointerPoint) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Vue Composable for stroke stabilization
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```vue
|
|
15
|
+
* <script setup lang="ts">
|
|
16
|
+
* import { useStabilizedPointer } from '@stroke-stabilizer/vue'
|
|
17
|
+
*
|
|
18
|
+
* const { process, reset } = useStabilizedPointer({
|
|
19
|
+
* level: 50,
|
|
20
|
+
* onPoint: (point) => {
|
|
21
|
+
* // Draw with stabilized point
|
|
22
|
+
* drawPoint(point)
|
|
23
|
+
* }
|
|
24
|
+
* })
|
|
25
|
+
*
|
|
26
|
+
* function handlePointerMove(e: PointerEvent) {
|
|
27
|
+
* process({
|
|
28
|
+
* x: e.clientX,
|
|
29
|
+
* y: e.clientY,
|
|
30
|
+
* pressure: e.pressure,
|
|
31
|
+
* timestamp: e.timeStamp
|
|
32
|
+
* })
|
|
33
|
+
* }
|
|
34
|
+
*
|
|
35
|
+
* function handlePointerUp() {
|
|
36
|
+
* reset()
|
|
37
|
+
* }
|
|
38
|
+
* </script>
|
|
39
|
+
*
|
|
40
|
+
* <template>
|
|
41
|
+
* <canvas @pointermove="handlePointerMove" @pointerup="handlePointerUp" />
|
|
42
|
+
* </template>
|
|
43
|
+
* ```
|
|
44
|
+
*/
|
|
45
|
+
export declare function useStabilizedPointer(options?: UseStabilizedPointerOptions): {
|
|
46
|
+
process: (point: PointerPoint) => PointerPoint | null;
|
|
47
|
+
processAll: (points: PointerPoint[]) => PointerPoint[];
|
|
48
|
+
flushBuffer: () => PointerPoint[];
|
|
49
|
+
reset: () => void;
|
|
50
|
+
addFilter: (filter: Filter) => void;
|
|
51
|
+
removeFilter: (type: string) => boolean;
|
|
52
|
+
updateFilter: <T>(type: string, params: Partial<T>) => boolean;
|
|
53
|
+
pointer: import("vue").ComputedRef<StabilizedPointer>;
|
|
54
|
+
filterCount: import("vue").ComputedRef<number>;
|
|
55
|
+
};
|
|
56
|
+
//# sourceMappingURL=useStabilizedPointer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useStabilizedPointer.d.ts","sourceRoot":"","sources":["../src/useStabilizedPointer.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EAEjB,KAAK,YAAY,EACjB,KAAK,MAAM,EACZ,MAAM,yBAAyB,CAAA;AAEhC,MAAM,WAAW,2BAA2B;IAC1C,8DAA8D;IAC9D,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,yCAAyC;IACzC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;CACxC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,2BAAgC;qBAsBjB,YAAY,KAAG,YAAY,GAAG,IAAI;yBAQ9B,YAAY,EAAE,KAAG,YAAY,EAAE;uBAUnC,YAAY,EAAE;iBAIpB,IAAI;wBAIK,MAAM,KAAG,IAAI;yBAKZ,MAAM,KAAG,OAAO;mBAMtB,CAAC,QAAQ,MAAM,UAAU,OAAO,CAAC,CAAC,CAAC,KAAG,OAAO;;;EAsBpE"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@stroke-stabilizer/vue",
|
|
3
|
+
"version": "0.1.3",
|
|
4
|
+
"description": "Vue composables for stroke stabilization",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.cjs",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.cjs",
|
|
13
|
+
"types": "./dist/index.d.ts"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist"
|
|
18
|
+
],
|
|
19
|
+
"scripts": {
|
|
20
|
+
"dev": "vite build --watch",
|
|
21
|
+
"build": "vite build && tsc --emitDeclarationOnly",
|
|
22
|
+
"typecheck": "tsc --noEmit"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@stroke-stabilizer/core": "workspace:*"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"vue": ">=3.0.0"
|
|
29
|
+
},
|
|
30
|
+
"devDependencies": {
|
|
31
|
+
"vue": "^3.4.0",
|
|
32
|
+
"vite": "^5.4.0",
|
|
33
|
+
"vite-plugin-dts": "^4.0.0"
|
|
34
|
+
},
|
|
35
|
+
"keywords": [
|
|
36
|
+
"vue",
|
|
37
|
+
"composable",
|
|
38
|
+
"stroke",
|
|
39
|
+
"stabilization",
|
|
40
|
+
"drawing",
|
|
41
|
+
"canvas"
|
|
42
|
+
],
|
|
43
|
+
"author": "usapopopooon <usapopopooon@gmail.com>",
|
|
44
|
+
"license": "MIT",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/usapopopooon/stroke-stabilizer"
|
|
48
|
+
},
|
|
49
|
+
"homepage": "https://github.com/usapopopooon/stroke-stabilizer",
|
|
50
|
+
"sideEffects": false
|
|
51
|
+
}
|