@riebeckite/plugin-text-fragment 0.0.31 → 0.0.33
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 +9 -5
- package/README_ja.md +9 -5
- package/dist/client.js +36 -13
- package/dist/index.d.ts +1 -1
- package/dist/index.js +38 -15
- package/dist/src/text-fragment.client.d.ts +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -7,6 +7,10 @@ you select in an article.
|
|
|
7
7
|
|
|
8
8
|
[日本語](./README_ja.md)
|
|
9
9
|
|
|
10
|
+
## Live example
|
|
11
|
+
|
|
12
|
+
Select any sentence on this page. The plugin adds a copy action that produces a URL containing a `#:~:text=` fragment for the selected text.
|
|
13
|
+
|
|
10
14
|
## Overview
|
|
11
15
|
|
|
12
16
|
Client-only plugin. On page load it installs one selection popover with two
|
|
@@ -21,17 +25,17 @@ actions:
|
|
|
21
25
|
|
|
22
26
|
```ts
|
|
23
27
|
import { defineConfig } from "@riebeckite/core";
|
|
24
|
-
import {
|
|
28
|
+
import { textFragment } from "@riebeckite/plugin-text-fragment";
|
|
25
29
|
|
|
26
30
|
export default defineConfig({
|
|
27
31
|
// ...
|
|
28
|
-
plugins: [
|
|
32
|
+
plugins: [textFragment()],
|
|
29
33
|
});
|
|
30
34
|
```
|
|
31
35
|
|
|
32
|
-
`
|
|
36
|
+
`textFragment()` registers `style.css` and the `initTextFragmentShare`
|
|
33
37
|
client entry, which the app calls during page initialization. Pass
|
|
34
|
-
`
|
|
38
|
+
`textFragment({ labels })` to override individual UI labels.
|
|
35
39
|
|
|
36
40
|
## Behavior
|
|
37
41
|
|
|
@@ -59,7 +63,7 @@ The fragment follows `#:~:text=[prefix-,]start[,end][,-suffix]`:
|
|
|
59
63
|
|
|
60
64
|
## API
|
|
61
65
|
|
|
62
|
-
- `
|
|
66
|
+
- `textFragment(options?)` — plugin factory; `options.labels` overrides
|
|
63
67
|
the UI labels
|
|
64
68
|
- `initTextFragmentShare(labels?)` — client initializer (also via
|
|
65
69
|
`@riebeckite/plugin-text-fragment/client`)
|
package/README_ja.md
CHANGED
|
@@ -6,6 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
[English](./README.md)
|
|
8
8
|
|
|
9
|
+
## 実行例
|
|
10
|
+
|
|
11
|
+
このページの任意の文を選択してください。選択したテキストへの `#:~:text=` フラグメントを含む URL をコピーする操作が追加されます。
|
|
12
|
+
|
|
9
13
|
## できること
|
|
10
14
|
|
|
11
15
|
クライアント専用のプラグインです。ページ表示時に、選択範囲のそばへ小さなポップオーバーを出し、次の二つをコピーできます。
|
|
@@ -17,15 +21,15 @@
|
|
|
17
21
|
|
|
18
22
|
```ts
|
|
19
23
|
import { defineConfig } from "@riebeckite/core";
|
|
20
|
-
import {
|
|
24
|
+
import { textFragment } from "@riebeckite/plugin-text-fragment";
|
|
21
25
|
|
|
22
26
|
export default defineConfig({
|
|
23
27
|
// ...
|
|
24
|
-
plugins: [
|
|
28
|
+
plugins: [textFragment()],
|
|
25
29
|
});
|
|
26
30
|
```
|
|
27
31
|
|
|
28
|
-
`
|
|
32
|
+
`textFragment()` はスタイルと `initTextFragmentShare` を登録します。初期化処理はアプリのページ初期化時に呼ばれます。`textFragment({ labels })` で UI ラベルだけを差し替えられます。
|
|
29
33
|
|
|
30
34
|
## 挙動
|
|
31
35
|
|
|
@@ -42,12 +46,12 @@ export default defineConfig({
|
|
|
42
46
|
- `,`、`-`、`&` はパーセントエンコードします(`%2C`、`%2D`、`%26`)。
|
|
43
47
|
- それ以外は UTF-8 単位でエンコードします(改行は `%0A`)。
|
|
44
48
|
- ページ URL に既存のハッシュがあるときは、それを外してからディレクティブを付けます。
|
|
45
|
-
- 200
|
|
49
|
+
- 約 200 文字を超える選択、または改行を含む選択は、先頭と末尾のトークンによる `start,end` に短縮します。
|
|
46
50
|
- 空または空白だけの選択は `""` を返します。
|
|
47
51
|
|
|
48
52
|
## 公開 API
|
|
49
53
|
|
|
50
|
-
- `
|
|
54
|
+
- `textFragment(options?)`:プラグインファクトリ。`options.labels` で UI ラベルを上書きする
|
|
51
55
|
- `initTextFragmentShare(labels?)`:ブラウザ初期化関数(`@riebeckite/plugin-text-fragment/client` からも読み込める)
|
|
52
56
|
- `encodeTextFragment(text)`:テキスト片をパーセントエンコードする
|
|
53
57
|
- `buildTextFragmentUrl(pageUrl, selection, options?)`:ディープリンクを作る。`options` は `{ prefix?, suffix? }`
|
package/dist/client.js
CHANGED
|
@@ -86,6 +86,7 @@ function initTextFragmentShare(labels = {}) {
|
|
|
86
86
|
popover.appendChild(status);
|
|
87
87
|
document.body.appendChild(popover);
|
|
88
88
|
let active = null;
|
|
89
|
+
const copyingTimers = /* @__PURE__ */ new Set();
|
|
89
90
|
let isCopying = false;
|
|
90
91
|
const hide = () => {
|
|
91
92
|
active = null;
|
|
@@ -118,18 +119,20 @@ function initTextFragmentShare(labels = {}) {
|
|
|
118
119
|
popover.hidden = false;
|
|
119
120
|
positionPopover(popover, selection.rect);
|
|
120
121
|
} finally {
|
|
121
|
-
window.setTimeout(() => {
|
|
122
|
+
const timer = window.setTimeout(() => {
|
|
123
|
+
copyingTimers.delete(timer);
|
|
122
124
|
isCopying = false;
|
|
123
125
|
}, 0);
|
|
126
|
+
copyingTimers.add(timer);
|
|
124
127
|
}
|
|
125
128
|
};
|
|
126
|
-
|
|
129
|
+
const onLinkButtonClick = () => {
|
|
127
130
|
void copy(
|
|
128
131
|
(selection) => buildTextFragmentUrl(resolvePageUrl(), selection),
|
|
129
132
|
resolved.linkCopiedLabel
|
|
130
133
|
);
|
|
131
|
-
}
|
|
132
|
-
|
|
134
|
+
};
|
|
135
|
+
const onQuoteButtonClick = () => {
|
|
133
136
|
void copy(
|
|
134
137
|
(selection) => buildQuoteMarkdown({
|
|
135
138
|
url: resolvePageUrl(),
|
|
@@ -138,23 +141,43 @@ function initTextFragmentShare(labels = {}) {
|
|
|
138
141
|
}),
|
|
139
142
|
resolved.quoteCopiedLabel
|
|
140
143
|
);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
document.addEventListener("mouseup", refresh);
|
|
145
|
-
document.addEventListener("keyup", refresh);
|
|
146
|
-
document.addEventListener("keydown", (event) => {
|
|
144
|
+
};
|
|
145
|
+
const onPopoverMouseDown = (event) => event.preventDefault();
|
|
146
|
+
const onKeyDown = (event) => {
|
|
147
147
|
if (event.key === "Escape" && !popover.hidden) {
|
|
148
148
|
event.preventDefault();
|
|
149
149
|
hide();
|
|
150
150
|
}
|
|
151
|
-
}
|
|
152
|
-
|
|
151
|
+
};
|
|
152
|
+
const onMouseDown = (event) => {
|
|
153
153
|
if (popover.hidden) return;
|
|
154
154
|
if (event.target instanceof Node && popover.contains(event.target)) return;
|
|
155
155
|
hide();
|
|
156
|
-
}
|
|
156
|
+
};
|
|
157
|
+
linkButton.addEventListener("click", onLinkButtonClick);
|
|
158
|
+
quoteButton.addEventListener("click", onQuoteButtonClick);
|
|
159
|
+
popover.addEventListener("mousedown", onPopoverMouseDown);
|
|
160
|
+
document.addEventListener("selectionchange", refresh);
|
|
161
|
+
document.addEventListener("mouseup", refresh);
|
|
162
|
+
document.addEventListener("keyup", refresh);
|
|
163
|
+
document.addEventListener("keydown", onKeyDown);
|
|
164
|
+
document.addEventListener("mousedown", onMouseDown);
|
|
157
165
|
window.addEventListener("scroll", hide, { passive: true });
|
|
166
|
+
return () => {
|
|
167
|
+
initialized = false;
|
|
168
|
+
for (const timer of copyingTimers) window.clearTimeout(timer);
|
|
169
|
+
copyingTimers.clear();
|
|
170
|
+
linkButton.removeEventListener("click", onLinkButtonClick);
|
|
171
|
+
quoteButton.removeEventListener("click", onQuoteButtonClick);
|
|
172
|
+
popover.removeEventListener("mousedown", onPopoverMouseDown);
|
|
173
|
+
document.removeEventListener("selectionchange", refresh);
|
|
174
|
+
document.removeEventListener("mouseup", refresh);
|
|
175
|
+
document.removeEventListener("keyup", refresh);
|
|
176
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
177
|
+
document.removeEventListener("mousedown", onMouseDown);
|
|
178
|
+
window.removeEventListener("scroll", hide);
|
|
179
|
+
popover.remove();
|
|
180
|
+
};
|
|
158
181
|
}
|
|
159
182
|
function createButton(label) {
|
|
160
183
|
const button = document.createElement("button");
|
package/dist/index.d.ts
CHANGED
|
@@ -9,4 +9,4 @@ export type TextFragmentPluginOptions = {
|
|
|
9
9
|
* Registers the client-only text fragment share plugin: a selection popover
|
|
10
10
|
* that copies a `#:~:text=` deep link or a Markdown quote.
|
|
11
11
|
*/
|
|
12
|
-
export declare function
|
|
12
|
+
export declare function textFragment(options?: TextFragmentPluginOptions): RiebeckitePlugin;
|
package/dist/index.js
CHANGED
|
@@ -93,6 +93,7 @@ function initTextFragmentShare(labels = {}) {
|
|
|
93
93
|
popover.appendChild(status);
|
|
94
94
|
document.body.appendChild(popover);
|
|
95
95
|
let active = null;
|
|
96
|
+
const copyingTimers = /* @__PURE__ */ new Set();
|
|
96
97
|
let isCopying = false;
|
|
97
98
|
const hide = () => {
|
|
98
99
|
active = null;
|
|
@@ -125,18 +126,20 @@ function initTextFragmentShare(labels = {}) {
|
|
|
125
126
|
popover.hidden = false;
|
|
126
127
|
positionPopover(popover, selection.rect);
|
|
127
128
|
} finally {
|
|
128
|
-
window.setTimeout(() => {
|
|
129
|
+
const timer = window.setTimeout(() => {
|
|
130
|
+
copyingTimers.delete(timer);
|
|
129
131
|
isCopying = false;
|
|
130
132
|
}, 0);
|
|
133
|
+
copyingTimers.add(timer);
|
|
131
134
|
}
|
|
132
135
|
};
|
|
133
|
-
|
|
136
|
+
const onLinkButtonClick = () => {
|
|
134
137
|
void copy(
|
|
135
138
|
(selection) => buildTextFragmentUrl(resolvePageUrl(), selection),
|
|
136
139
|
resolved.linkCopiedLabel
|
|
137
140
|
);
|
|
138
|
-
}
|
|
139
|
-
|
|
141
|
+
};
|
|
142
|
+
const onQuoteButtonClick = () => {
|
|
140
143
|
void copy(
|
|
141
144
|
(selection) => buildQuoteMarkdown({
|
|
142
145
|
url: resolvePageUrl(),
|
|
@@ -145,23 +148,43 @@ function initTextFragmentShare(labels = {}) {
|
|
|
145
148
|
}),
|
|
146
149
|
resolved.quoteCopiedLabel
|
|
147
150
|
);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
document.addEventListener("mouseup", refresh);
|
|
152
|
-
document.addEventListener("keyup", refresh);
|
|
153
|
-
document.addEventListener("keydown", (event) => {
|
|
151
|
+
};
|
|
152
|
+
const onPopoverMouseDown = (event) => event.preventDefault();
|
|
153
|
+
const onKeyDown = (event) => {
|
|
154
154
|
if (event.key === "Escape" && !popover.hidden) {
|
|
155
155
|
event.preventDefault();
|
|
156
156
|
hide();
|
|
157
157
|
}
|
|
158
|
-
}
|
|
159
|
-
|
|
158
|
+
};
|
|
159
|
+
const onMouseDown = (event) => {
|
|
160
160
|
if (popover.hidden) return;
|
|
161
161
|
if (event.target instanceof Node && popover.contains(event.target)) return;
|
|
162
162
|
hide();
|
|
163
|
-
}
|
|
163
|
+
};
|
|
164
|
+
linkButton.addEventListener("click", onLinkButtonClick);
|
|
165
|
+
quoteButton.addEventListener("click", onQuoteButtonClick);
|
|
166
|
+
popover.addEventListener("mousedown", onPopoverMouseDown);
|
|
167
|
+
document.addEventListener("selectionchange", refresh);
|
|
168
|
+
document.addEventListener("mouseup", refresh);
|
|
169
|
+
document.addEventListener("keyup", refresh);
|
|
170
|
+
document.addEventListener("keydown", onKeyDown);
|
|
171
|
+
document.addEventListener("mousedown", onMouseDown);
|
|
164
172
|
window.addEventListener("scroll", hide, { passive: true });
|
|
173
|
+
return () => {
|
|
174
|
+
initialized = false;
|
|
175
|
+
for (const timer of copyingTimers) window.clearTimeout(timer);
|
|
176
|
+
copyingTimers.clear();
|
|
177
|
+
linkButton.removeEventListener("click", onLinkButtonClick);
|
|
178
|
+
quoteButton.removeEventListener("click", onQuoteButtonClick);
|
|
179
|
+
popover.removeEventListener("mousedown", onPopoverMouseDown);
|
|
180
|
+
document.removeEventListener("selectionchange", refresh);
|
|
181
|
+
document.removeEventListener("mouseup", refresh);
|
|
182
|
+
document.removeEventListener("keyup", refresh);
|
|
183
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
184
|
+
document.removeEventListener("mousedown", onMouseDown);
|
|
185
|
+
window.removeEventListener("scroll", hide);
|
|
186
|
+
popover.remove();
|
|
187
|
+
};
|
|
165
188
|
}
|
|
166
189
|
function createButton(label) {
|
|
167
190
|
const button = document.createElement("button");
|
|
@@ -249,7 +272,7 @@ function copyWithExecCommand(value) {
|
|
|
249
272
|
}
|
|
250
273
|
|
|
251
274
|
// index.ts
|
|
252
|
-
function
|
|
275
|
+
function textFragment(options = {}) {
|
|
253
276
|
return definePlugin({
|
|
254
277
|
name: "text-fragment",
|
|
255
278
|
assets: [
|
|
@@ -277,5 +300,5 @@ export {
|
|
|
277
300
|
buildTextFragmentUrl,
|
|
278
301
|
encodeTextFragment,
|
|
279
302
|
initTextFragmentShare,
|
|
280
|
-
|
|
303
|
+
textFragment
|
|
281
304
|
};
|
|
@@ -14,4 +14,4 @@ export declare const DEFAULT_TEXT_FRAGMENT_LABELS: TextFragmentLabels;
|
|
|
14
14
|
* Client-only: returns immediately when there is no `document`, and only wires
|
|
15
15
|
* its listeners once per page.
|
|
16
16
|
*/
|
|
17
|
-
export declare function initTextFragmentShare(labels?: Partial<TextFragmentLabels>): void;
|
|
17
|
+
export declare function initTextFragmentShare(labels?: Partial<TextFragmentLabels>): (() => void) | undefined;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@riebeckite/plugin-text-fragment",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.33",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"./style.css": "./style.css"
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@riebeckite/core": "0.0.
|
|
24
|
+
"@riebeckite/core": "0.0.33"
|
|
25
25
|
},
|
|
26
26
|
"devDependencies": {
|
|
27
27
|
"tsx": "^4.22.4"
|