copy_tuner_client 2.2.1 → 2.4.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.
- checksums.yaml +4 -4
- data/CLAUDE.md +10 -0
- data/app/assets/javascripts/copytuner.js +69 -78
- data/docs/poller-startup.md +108 -0
- data/index.html +54 -0
- data/lib/copy_tuner_client/fork_hook.rb +53 -0
- data/lib/copy_tuner_client/poller.rb +87 -23
- data/lib/copy_tuner_client/process_guard.rb +13 -4
- data/lib/copy_tuner_client/queue_with_timeout.rb +8 -2
- data/lib/copy_tuner_client/version.rb +1 -1
- data/spec/copy_tuner_client/fork_hook_spec.rb +171 -0
- data/spec/copy_tuner_client/poller_spec.rb +148 -1
- data/spec/copy_tuner_client/process_guard_spec.rb +8 -0
- data/spec/copy_tuner_client/queue_with_timeout_spec.rb +27 -0
- data/spec/spec_helper.rb +6 -0
- data/src/copyray-overlay.ts +54 -47
- data/src/copytuner-bar.ts +0 -11
- data/src/copytuner-root.ts +31 -0
- data/src/main.ts +22 -12
- data/src/styles.ts +62 -41
- data/src/util.ts +1 -36
- metadata +7 -2
data/src/main.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/* eslint-disable no-console */
|
|
2
2
|
import { CopyrayOverlay } from './copyray-overlay'
|
|
3
3
|
import { CopytunerBar } from './copytuner-bar'
|
|
4
|
+
import { CopytunerRoot } from './copytuner-root'
|
|
4
5
|
import { isMac } from './util'
|
|
5
6
|
|
|
6
7
|
declare global {
|
|
@@ -18,38 +19,47 @@ declare global {
|
|
|
18
19
|
|
|
19
20
|
customElements.define('copytuner-bar', CopytunerBar)
|
|
20
21
|
customElements.define('copyray-overlay', CopyrayOverlay)
|
|
22
|
+
customElements.define('copytuner-root', CopytunerRoot)
|
|
21
23
|
|
|
22
24
|
const start = () => {
|
|
23
25
|
const { url, data, keysSkipped } = window.CopyTuner
|
|
24
26
|
const onOpen = (key: string) => window.open(`${url}/blurbs/${key}/edit`)
|
|
25
27
|
|
|
26
|
-
const
|
|
27
|
-
document.body.append(
|
|
28
|
-
bar.init({ url, data, keysSkipped: Boolean(keysSkipped), onOpen })
|
|
28
|
+
const root = document.createElement('copytuner-root') as CopytunerRoot
|
|
29
|
+
document.body.append(root)
|
|
29
30
|
|
|
31
|
+
const bar = document.createElement('copytuner-bar') as CopytunerBar
|
|
30
32
|
const overlay = document.createElement('copyray-overlay') as CopyrayOverlay
|
|
31
33
|
overlay.onOpen = onOpen
|
|
32
|
-
|
|
34
|
+
root.dialog.append(overlay, bar)
|
|
35
|
+
bar.init({ url, data, keysSkipped: Boolean(keysSkipped), onOpen })
|
|
33
36
|
|
|
34
37
|
const show = () => {
|
|
38
|
+
// display: none のままでは searchBox にフォーカスが当たらないので dialog を先に開く
|
|
39
|
+
root.dialog.showModal()
|
|
35
40
|
overlay.show()
|
|
36
41
|
bar.show()
|
|
37
42
|
}
|
|
38
43
|
const hide = () => {
|
|
44
|
+
root.dialog.close()
|
|
45
|
+
// close イベントを待たず同期で後始末する。イベントは queued task 発火で、
|
|
46
|
+
// 自動テスト環境など発火が遅れる/落ちる状況があるため、ここを唯一の頼りにしない
|
|
39
47
|
overlay.hide()
|
|
40
|
-
bar.hide()
|
|
41
48
|
}
|
|
42
|
-
const toggle = () => (
|
|
49
|
+
const toggle = () => (root.dialog.open ? hide() : show())
|
|
43
50
|
|
|
44
|
-
|
|
51
|
+
root.onToggle = toggle
|
|
52
|
+
overlay.onClose = hide
|
|
45
53
|
window.CopyTuner.toggle = toggle
|
|
46
54
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
55
|
+
// Escape のようにこちらの hide() を経由しない閉じ方の受け皿。overlay.hide() は冪等なので二重でも問題ない
|
|
56
|
+
root.dialog.addEventListener('close', () => {
|
|
57
|
+
// 閉じた直後に開き直された場合、遅れて届いたこのイベントで後始末してはいけない
|
|
58
|
+
if (root.dialog.open) return
|
|
59
|
+
overlay.hide()
|
|
60
|
+
})
|
|
52
61
|
|
|
62
|
+
document.addEventListener('keydown', (event) => {
|
|
53
63
|
if (((isMac && event.metaKey) || (!isMac && event.ctrlKey)) && event.shiftKey && event.key.toLowerCase() === 'k') {
|
|
54
64
|
toggle()
|
|
55
65
|
}
|
data/src/styles.ts
CHANGED
|
@@ -1,6 +1,57 @@
|
|
|
1
1
|
// 各 custom element の Shadow root に <style> として注入する CSS。
|
|
2
2
|
// Shadow DOM のスタイル隔離が効くため、旧 copyray.css にあった #copyray-overlay * のグローバルリセットは不要。
|
|
3
3
|
|
|
4
|
+
// コンテナ(<copytuner-root>)のスタイル。
|
|
5
|
+
// dialog の UA デフォルト(中央寄せ・白背景・枠線・padding・暗転)を全て打ち消し、
|
|
6
|
+
// viewport 全面を覆う透明なコンテナにする。暗転は overlay 側の .backdrop が担う。
|
|
7
|
+
export const ROOT_STYLES = `
|
|
8
|
+
/* transform / filter / perspective / contain は置かない(理由は OVERLAY_STYLES のコメント参照) */
|
|
9
|
+
dialog {
|
|
10
|
+
position: fixed;
|
|
11
|
+
inset: 0;
|
|
12
|
+
width: 100%;
|
|
13
|
+
height: 100%;
|
|
14
|
+
max-width: none;
|
|
15
|
+
max-height: none;
|
|
16
|
+
margin: 0;
|
|
17
|
+
border: none;
|
|
18
|
+
padding: 0;
|
|
19
|
+
background: transparent;
|
|
20
|
+
color: inherit;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
dialog::backdrop {
|
|
24
|
+
background: transparent;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.toggle-button {
|
|
28
|
+
display: block;
|
|
29
|
+
position: fixed;
|
|
30
|
+
left: 0;
|
|
31
|
+
bottom: 0;
|
|
32
|
+
color: white;
|
|
33
|
+
background: black;
|
|
34
|
+
padding: 12px 16px;
|
|
35
|
+
border-radius: 0 10px 0 0;
|
|
36
|
+
opacity: 0;
|
|
37
|
+
transition: opacity 0.6s ease-in-out;
|
|
38
|
+
z-index: 10000;
|
|
39
|
+
font-size: 12px;
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
text-decoration: none;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.toggle-button:hover {
|
|
45
|
+
opacity: 1;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
@media screen and (max-width: 480px) {
|
|
49
|
+
.toggle-button {
|
|
50
|
+
display: none;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
`
|
|
54
|
+
|
|
4
55
|
// ツールバー(<copytuner-bar>)のスタイル。:host にバー本体のレイアウトを定義する。
|
|
5
56
|
export const BAR_STYLES = `
|
|
6
57
|
:host {
|
|
@@ -20,10 +71,6 @@ export const BAR_STYLES = `
|
|
|
20
71
|
box-sizing: border-box;
|
|
21
72
|
}
|
|
22
73
|
|
|
23
|
-
:host([hidden]) {
|
|
24
|
-
display: none;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
74
|
.log-menu {
|
|
28
75
|
position: fixed;
|
|
29
76
|
left: 0;
|
|
@@ -109,22 +156,19 @@ export const BAR_STYLES = `
|
|
|
109
156
|
`
|
|
110
157
|
|
|
111
158
|
// オーバーレイ(<copyray-overlay>)のスタイル。
|
|
112
|
-
//
|
|
113
|
-
//
|
|
114
|
-
//
|
|
159
|
+
// specimen は getBoundingClientRect() の viewport 座標をそのまま使うため、
|
|
160
|
+
// dialog / :host / specimen のコンテナ div のいずれにも
|
|
161
|
+
// transform / filter / perspective / contain を置かない。
|
|
162
|
+
// 置くとそれが containing block になり、fixed な specimen が viewport 基準から外れる。
|
|
163
|
+
// :host の position: absolute と width/height: 0 は、インラインの custom element が
|
|
164
|
+
// dialog のフローに line box を作らないようにするため(fixed の containing block は作らない)。
|
|
115
165
|
export const OVERLAY_STYLES = `
|
|
116
166
|
:host {
|
|
117
167
|
position: absolute;
|
|
118
|
-
top: 0;
|
|
119
|
-
left: 0;
|
|
120
168
|
width: 0;
|
|
121
169
|
height: 0;
|
|
122
170
|
}
|
|
123
171
|
|
|
124
|
-
:host([hidden]) {
|
|
125
|
-
display: none;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
172
|
.backdrop {
|
|
129
173
|
position: fixed;
|
|
130
174
|
inset: 0;
|
|
@@ -137,7 +181,7 @@ export const OVERLAY_STYLES = `
|
|
|
137
181
|
}
|
|
138
182
|
|
|
139
183
|
.specimen {
|
|
140
|
-
position:
|
|
184
|
+
position: fixed;
|
|
141
185
|
background: rgba(255, 50, 50, 0.1);
|
|
142
186
|
outline: 1px solid rgba(255, 50, 50, 0.8);
|
|
143
187
|
outline-offset: -1px;
|
|
@@ -153,6 +197,10 @@ export const OVERLAY_STYLES = `
|
|
|
153
197
|
background: rgba(255, 50, 50, 0.4);
|
|
154
198
|
}
|
|
155
199
|
|
|
200
|
+
.specimen[hidden] {
|
|
201
|
+
display: none;
|
|
202
|
+
}
|
|
203
|
+
|
|
156
204
|
.specimen-handle {
|
|
157
205
|
float: left;
|
|
158
206
|
margin: 0 2px 2px 0;
|
|
@@ -162,31 +210,4 @@ export const OVERLAY_STYLES = `
|
|
|
162
210
|
font-size: 10px;
|
|
163
211
|
cursor: pointer;
|
|
164
212
|
}
|
|
165
|
-
|
|
166
|
-
.toggle-button {
|
|
167
|
-
display: block;
|
|
168
|
-
position: fixed;
|
|
169
|
-
left: 0;
|
|
170
|
-
bottom: 0;
|
|
171
|
-
color: white;
|
|
172
|
-
background: black;
|
|
173
|
-
padding: 12px 16px;
|
|
174
|
-
border-radius: 0 10px 0 0;
|
|
175
|
-
opacity: 0;
|
|
176
|
-
transition: opacity 0.6s ease-in-out;
|
|
177
|
-
z-index: 10000;
|
|
178
|
-
font-size: 12px;
|
|
179
|
-
cursor: pointer;
|
|
180
|
-
text-decoration: none;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
.toggle-button:hover {
|
|
184
|
-
opacity: 1;
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
@media screen and (max-width: 480px) {
|
|
188
|
-
.toggle-button {
|
|
189
|
-
display: none;
|
|
190
|
-
}
|
|
191
|
-
}
|
|
192
213
|
`
|
data/src/util.ts
CHANGED
|
@@ -1,40 +1,5 @@
|
|
|
1
1
|
const isMac = navigator.platform.toUpperCase().includes('MAC')
|
|
2
2
|
|
|
3
|
-
// @ts-expect-error TS7006
|
|
4
|
-
const isVisible = (element) => !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length > 0)
|
|
5
|
-
|
|
6
|
-
// @ts-expect-error TS7006
|
|
7
|
-
const getOffset = (elment) => {
|
|
8
|
-
const box = elment.getBoundingClientRect()
|
|
9
|
-
|
|
10
|
-
return {
|
|
11
|
-
top: box.top + (window.pageYOffset - document.documentElement.clientTop),
|
|
12
|
-
left: box.left + (window.pageXOffset - document.documentElement.clientLeft),
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
// @ts-expect-error TS7006
|
|
17
|
-
const computeBoundingBox = (element) => {
|
|
18
|
-
if (!isVisible(element)) {
|
|
19
|
-
return null
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const boxFrame = getOffset(element)
|
|
23
|
-
// @ts-expect-error TS2339
|
|
24
|
-
boxFrame.right = boxFrame.left + element.offsetWidth
|
|
25
|
-
// @ts-expect-error TS2339
|
|
26
|
-
boxFrame.bottom = boxFrame.top + element.offsetHeight
|
|
27
|
-
|
|
28
|
-
return {
|
|
29
|
-
left: boxFrame.left,
|
|
30
|
-
top: boxFrame.top,
|
|
31
|
-
// @ts-expect-error TS2339
|
|
32
|
-
width: boxFrame.right - boxFrame.left,
|
|
33
|
-
// @ts-expect-error TS2339
|
|
34
|
-
height: boxFrame.bottom - boxFrame.top,
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
3
|
const debounce = <A extends unknown[]>(fn: (...args: A) => void, wait: number) => {
|
|
39
4
|
let timer: ReturnType<typeof setTimeout> | undefined
|
|
40
5
|
return (...args: A) => {
|
|
@@ -43,4 +8,4 @@ const debounce = <A extends unknown[]>(fn: (...args: A) => void, wait: number) =
|
|
|
43
8
|
}
|
|
44
9
|
}
|
|
45
10
|
|
|
46
|
-
export {
|
|
11
|
+
export { debounce, isMac }
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: copy_tuner_client
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SonicGarden
|
|
@@ -200,6 +200,7 @@ files:
|
|
|
200
200
|
- app/assets/javascripts/copytuner.js
|
|
201
201
|
- biome.json
|
|
202
202
|
- copy_tuner_client.gemspec
|
|
203
|
+
- docs/poller-startup.md
|
|
203
204
|
- gemfiles/8.0.gemfile
|
|
204
205
|
- gemfiles/8.1.gemfile
|
|
205
206
|
- gemfiles/main.gemfile
|
|
@@ -215,6 +216,7 @@ files:
|
|
|
215
216
|
- lib/copy_tuner_client/dotted_hash.rb
|
|
216
217
|
- lib/copy_tuner_client/engine.rb
|
|
217
218
|
- lib/copy_tuner_client/errors.rb
|
|
219
|
+
- lib/copy_tuner_client/fork_hook.rb
|
|
218
220
|
- lib/copy_tuner_client/helper_extension.rb
|
|
219
221
|
- lib/copy_tuner_client/i18n_backend.rb
|
|
220
222
|
- lib/copy_tuner_client/i18n_compat.rb
|
|
@@ -251,11 +253,13 @@ files:
|
|
|
251
253
|
- spec/copy_tuner_client/copyray_middleware_spec.rb
|
|
252
254
|
- spec/copy_tuner_client/copyray_spec.rb
|
|
253
255
|
- spec/copy_tuner_client/dotted_hash_spec.rb
|
|
256
|
+
- spec/copy_tuner_client/fork_hook_spec.rb
|
|
254
257
|
- spec/copy_tuner_client/helper_extension_spec.rb
|
|
255
258
|
- spec/copy_tuner_client/i18n_backend_spec.rb
|
|
256
259
|
- spec/copy_tuner_client/poller_spec.rb
|
|
257
260
|
- spec/copy_tuner_client/prefixed_logger_spec.rb
|
|
258
261
|
- spec/copy_tuner_client/process_guard_spec.rb
|
|
262
|
+
- spec/copy_tuner_client/queue_with_timeout_spec.rb
|
|
259
263
|
- spec/copy_tuner_client/request_sync_spec.rb
|
|
260
264
|
- spec/copy_tuner_client/translation_log_spec.rb
|
|
261
265
|
- spec/copy_tuner_client/warden_integration_spec.rb
|
|
@@ -273,6 +277,7 @@ files:
|
|
|
273
277
|
- spec/support/writing_cache.rb
|
|
274
278
|
- src/copyray-overlay.ts
|
|
275
279
|
- src/copytuner-bar.ts
|
|
280
|
+
- src/copytuner-root.ts
|
|
276
281
|
- src/main.ts
|
|
277
282
|
- src/styles.ts
|
|
278
283
|
- src/util.ts
|
|
@@ -302,7 +307,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
302
307
|
- !ruby/object:Gem::Version
|
|
303
308
|
version: '0'
|
|
304
309
|
requirements: []
|
|
305
|
-
rubygems_version: 4.0.
|
|
310
|
+
rubygems_version: 4.0.10
|
|
306
311
|
specification_version: 4
|
|
307
312
|
summary: Client for the CopyTuner copy management service
|
|
308
313
|
test_files: []
|