hotkey-engine 0.0.0-stage → 0.1.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 +343 -2
- package/dist/fonts/Vazir-Bold.woff2 +0 -0
- package/dist/fonts/Vazir-Regular.woff2 +0 -0
- package/dist/index.cjs +415 -0
- package/dist/index.d.cts +89 -0
- package/dist/index.d.ts +89 -0
- package/dist/index.js +380 -0
- package/dist/styles.css +352 -0
- package/package.json +65 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 HotkeyEngine contributors
|
|
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
CHANGED
|
@@ -1,3 +1,344 @@
|
|
|
1
|
-
#
|
|
1
|
+
# hotkey-engine
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
hotkey-engine یک موتور میانبر کیبورد برای React 18+ و Next.js 13+ است. API بدون رابط کاربری، دکمهٔ شناور و پنجرهٔ آماده برای مدیریت میانبرها را در یک پکیج ارائه میکند. کاربر میتواند میانبرها را ویرایش، غیرفعال یا به پیشفرض بازنشانی کند و انتخاب او در مرورگر ذخیره میشود.
|
|
4
|
+
|
|
5
|
+
## امکانات
|
|
6
|
+
|
|
7
|
+
- میانبرهای فیزیکی و مستقل از زبان کیبورد؛ تغییر زبان کیبورد، Ctrl + K را تغییر نمیدهد.
|
|
8
|
+
- نگاشت mod به ⌘ Command در macOS و Ctrl در Windows/Linux.
|
|
9
|
+
- Scopeهای اولویتدار، جلوگیری از تداخل و تشخیص میانبرهای رزرو مرورگر/سیستمعامل.
|
|
10
|
+
- جلوگیری از اجرا داخل input، textarea و contenteditable، مگر با اجازهٔ صریح هر Action.
|
|
11
|
+
- ذخیرهسازی نسخهدار در localStorage و سازگاری با SSR.
|
|
12
|
+
- پنجرهٔ قابل دسترس: focus trap، بازگردانی focus، Escape و roleهای مناسب.
|
|
13
|
+
- رابط آماده با تم light، dark و system، بههمراه انگلیسی/LTR و فارسی/RTL.
|
|
14
|
+
- CSS مستقل و قابل شخصیسازی، بدون وابستگی UI خارجی.
|
|
15
|
+
|
|
16
|
+
## نصب
|
|
17
|
+
|
|
18
|
+
~~~bash
|
|
19
|
+
npm install hotkey-engine
|
|
20
|
+
~~~
|
|
21
|
+
|
|
22
|
+
CSS را یکبار، پس از CSS پایهٔ پروژه، import کنید:
|
|
23
|
+
|
|
24
|
+
~~~tsx
|
|
25
|
+
import "hotkey-engine/styles.css";
|
|
26
|
+
~~~
|
|
27
|
+
|
|
28
|
+
## شروع سریع در React
|
|
29
|
+
|
|
30
|
+
~~~tsx
|
|
31
|
+
import {
|
|
32
|
+
HotkeyProvider,
|
|
33
|
+
HotkeySettingsTrigger,
|
|
34
|
+
type HotkeyAction,
|
|
35
|
+
} from "hotkey-engine";
|
|
36
|
+
import "hotkey-engine/styles.css";
|
|
37
|
+
|
|
38
|
+
const actions: HotkeyAction[] = [
|
|
39
|
+
{
|
|
40
|
+
id: "site-search",
|
|
41
|
+
label: "Open site search",
|
|
42
|
+
defaultShortcut: "mod+k",
|
|
43
|
+
editable: true,
|
|
44
|
+
onTrigger: () => document.getElementById("site-search")?.focus(),
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
id: "save-draft",
|
|
48
|
+
label: "Save draft",
|
|
49
|
+
defaultShortcut: "mod+s",
|
|
50
|
+
editable: true,
|
|
51
|
+
preventDefault: true,
|
|
52
|
+
onTrigger: () => saveDraft(),
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
export function App() {
|
|
57
|
+
return (
|
|
58
|
+
<HotkeyProvider actions={actions}>
|
|
59
|
+
<input id="site-search" placeholder="Search" />
|
|
60
|
+
<HotkeySettingsTrigger>Keyboard shortcuts</HotkeySettingsTrigger>
|
|
61
|
+
</HotkeyProvider>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
~~~
|
|
65
|
+
|
|
66
|
+
دکمهٔ شناور تنظیمات بهصورت پیشفرض پایینِ چپ صفحه است. HotkeySettingsTrigger اختیاری است و برای متصلکردن دکمه یا منوی اختصاصی شما به پنجرهٔ تنظیمات استفاده میشود.
|
|
67
|
+
|
|
68
|
+
## استفاده در Next.js 13+ (App Router)
|
|
69
|
+
|
|
70
|
+
Provider را در یک Client Component بگذارید. پکیج هنگام SSR به window یا localStorage دسترسی ندارد.
|
|
71
|
+
|
|
72
|
+
~~~tsx
|
|
73
|
+
// app/shortcut-provider.tsx
|
|
74
|
+
"use client";
|
|
75
|
+
|
|
76
|
+
import { HotkeyProvider, type HotkeyAction } from "hotkey-engine";
|
|
77
|
+
|
|
78
|
+
const actions: HotkeyAction[] = [
|
|
79
|
+
{
|
|
80
|
+
id: "search",
|
|
81
|
+
label: "Open search",
|
|
82
|
+
defaultShortcut: "mod+k",
|
|
83
|
+
editable: true,
|
|
84
|
+
onTrigger: () => console.log("open search"),
|
|
85
|
+
},
|
|
86
|
+
];
|
|
87
|
+
|
|
88
|
+
export function ShortcutProvider({ children }: { children: React.ReactNode }) {
|
|
89
|
+
return <HotkeyProvider actions={actions}>{children}</HotkeyProvider>;
|
|
90
|
+
}
|
|
91
|
+
~~~
|
|
92
|
+
|
|
93
|
+
~~~tsx
|
|
94
|
+
// app/layout.tsx
|
|
95
|
+
import "hotkey-engine/styles.css";
|
|
96
|
+
import { ShortcutProvider } from "./shortcut-provider";
|
|
97
|
+
|
|
98
|
+
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
99
|
+
return (
|
|
100
|
+
<html lang="en">
|
|
101
|
+
<body><ShortcutProvider>{children}</ShortcutProvider></body>
|
|
102
|
+
</html>
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
~~~
|
|
106
|
+
|
|
107
|
+
## تعریف Action
|
|
108
|
+
|
|
109
|
+
~~~ts
|
|
110
|
+
type HotkeyAction = {
|
|
111
|
+
id: string;
|
|
112
|
+
label: string;
|
|
113
|
+
defaultShortcut: string;
|
|
114
|
+
onTrigger: (event: KeyboardEvent) => void;
|
|
115
|
+
editable?: boolean;
|
|
116
|
+
scope?: string;
|
|
117
|
+
allowInEditable?: boolean;
|
|
118
|
+
preventDefault?: boolean;
|
|
119
|
+
};
|
|
120
|
+
~~~
|
|
121
|
+
|
|
122
|
+
| فیلد | توضیح |
|
|
123
|
+
| --- | --- |
|
|
124
|
+
| id | شناسهٔ یکتا و پایدار. |
|
|
125
|
+
| label | نام Action در پنجرهٔ تنظیمات. |
|
|
126
|
+
| defaultShortcut | مانند mod+k، ctrl+shift+n یا alt+arrowleft. |
|
|
127
|
+
| onTrigger | تابع اجراشونده پس از تطابق میانبر. |
|
|
128
|
+
| editable | اگر false باشد، کاربر نمیتواند آن را ویرایش یا غیرفعال کند. |
|
|
129
|
+
| scope | نام Scope؛ اگر مشخص نشود global است. |
|
|
130
|
+
| allowInEditable | اجازهٔ اجرا هنگام تمرکز روی input، textarea یا contenteditable. پیشفرض: false. |
|
|
131
|
+
| preventDefault | پیش از اجرا event.preventDefault() را فراخوانی میکند. |
|
|
132
|
+
|
|
133
|
+
کلیدهای پشتیبانیشده: حروف، اعداد، F1 تا F12، enter، escape، space، tab، backspace، delete، arrowup، arrowdown، arrowleft، arrowright و slash. Modifierها: ctrl، alt، shift، meta و mod.
|
|
134
|
+
|
|
135
|
+
## باز و بسته کردن پنجره
|
|
136
|
+
|
|
137
|
+
~~~tsx
|
|
138
|
+
import { useHotkeySettings } from "hotkey-engine";
|
|
139
|
+
|
|
140
|
+
function ShortcutMenuItem() {
|
|
141
|
+
const { openSettings, closeSettings, open } = useHotkeySettings();
|
|
142
|
+
|
|
143
|
+
return (
|
|
144
|
+
<>
|
|
145
|
+
<button onClick={openSettings}>Manage shortcuts</button>
|
|
146
|
+
{open && <button onClick={closeSettings}>Close settings</button>}
|
|
147
|
+
</>
|
|
148
|
+
);
|
|
149
|
+
}
|
|
150
|
+
~~~
|
|
151
|
+
|
|
152
|
+
useHotkeySettings() باید زیر HotkeyProvider استفاده شود.
|
|
153
|
+
|
|
154
|
+
## Scope و اولویت
|
|
155
|
+
|
|
156
|
+
میانبرهای Scope فعال از global اولویت بیشتری دارند. اگر چند Scope فعال باشند، آخرین Scope فعالشده اول بررسی میشود.
|
|
157
|
+
|
|
158
|
+
~~~tsx
|
|
159
|
+
import { useHotkeyScope } from "hotkey-engine";
|
|
160
|
+
|
|
161
|
+
function Editor() {
|
|
162
|
+
useHotkeyScope("editor");
|
|
163
|
+
return <textarea />;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const actions = [
|
|
167
|
+
{ id: "global-save", label: "Save", defaultShortcut: "mod+s", onTrigger: saveSite },
|
|
168
|
+
{ id: "editor-save", label: "Save editor", defaultShortcut: "mod+s", scope: "editor", onTrigger: saveEditor },
|
|
169
|
+
];
|
|
170
|
+
~~~
|
|
171
|
+
|
|
172
|
+
دو Action با یک میانبر در Scopeهای همپوشان قابل ذخیره نیستند و پنجره، تداخل را به کاربر اعلام میکند.
|
|
173
|
+
|
|
174
|
+
## میانبرهای رزرو مرورگر و سیستمعامل
|
|
175
|
+
|
|
176
|
+
بهصورت پیشفرض، کاربر نمیتواند Ctrl/Cmd + K، L، R، W، T، N، P یا Alt + F4 را در Recorder ثبت کند؛ اینها معمولاً به مرورگر یا سیستمعامل تعلق دارند. پیام خطا کنار همان Action نشان داده میشود و میانبر قبلی حفظ میشود.
|
|
177
|
+
|
|
178
|
+
اگر توسعهدهنده آگاهانه بخواهد این امکان را بدهد:
|
|
179
|
+
|
|
180
|
+
~~~tsx
|
|
181
|
+
<HotkeyProvider actions={actions} allowReservedShortcuts>
|
|
182
|
+
<App />
|
|
183
|
+
</HotkeyProvider>
|
|
184
|
+
~~~
|
|
185
|
+
|
|
186
|
+
در این حالت، پکیج برای میانبر رزروشده preventDefault() اجرا میکند تا سایت بیشترین اولویت ممکن را داشته باشد. با این حال، مرورگر یا سیستمعامل ممکن است همچنان فرمان را کنترل کند؛ override آن در وب تضمینپذیر نیست.
|
|
187
|
+
|
|
188
|
+
## تم
|
|
189
|
+
|
|
190
|
+
~~~tsx
|
|
191
|
+
<HotkeyProvider actions={actions} theme="dark">
|
|
192
|
+
<App />
|
|
193
|
+
</HotkeyProvider>
|
|
194
|
+
~~~
|
|
195
|
+
|
|
196
|
+
| مقدار | رفتار |
|
|
197
|
+
| --- | --- |
|
|
198
|
+
| light | رابط روشن. |
|
|
199
|
+
| dark | رابط تیره. |
|
|
200
|
+
| system | پیشفرض؛ براساس prefers-color-scheme. |
|
|
201
|
+
|
|
202
|
+
تم فقط UI پکیج را تغییر میدهد و تم سایت شما را دستکاری نمیکند.
|
|
203
|
+
|
|
204
|
+
## زبان و RTL
|
|
205
|
+
|
|
206
|
+
انگلیسی/LTR پیشفرض است. برای فارسی، locale="fa" بگذارید؛ پنجره RTL میشود و فونت Vazir داخلی برای متن فارسی استفاده میشود.
|
|
207
|
+
|
|
208
|
+
~~~tsx
|
|
209
|
+
<HotkeyProvider actions={actions} locale="fa">
|
|
210
|
+
<App />
|
|
211
|
+
</HotkeyProvider>
|
|
212
|
+
~~~
|
|
213
|
+
|
|
214
|
+
برای تغییر زبان در زمان اجرا:
|
|
215
|
+
|
|
216
|
+
~~~tsx
|
|
217
|
+
import { useHotkeyLocale } from "hotkey-engine";
|
|
218
|
+
|
|
219
|
+
function LanguageSwitch() {
|
|
220
|
+
const { locale, setLocale } = useHotkeyLocale();
|
|
221
|
+
return (
|
|
222
|
+
<button onClick={() => setLocale(locale === "en" ? "fa" : "en")}>
|
|
223
|
+
{locale === "en" ? "فارسی" : "English"}
|
|
224
|
+
</button>
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
~~~
|
|
228
|
+
|
|
229
|
+
برای تعیین جهت مستقل از زبان، از dir="rtl" یا dir="ltr" استفاده کنید.
|
|
230
|
+
|
|
231
|
+
## دکمهٔ شناور
|
|
232
|
+
|
|
233
|
+
~~~tsx
|
|
234
|
+
<HotkeyProvider
|
|
235
|
+
actions={actions}
|
|
236
|
+
showFloatingButton
|
|
237
|
+
position="bottom-right"
|
|
238
|
+
offset={{ x: 32, y: 24 }}
|
|
239
|
+
zIndex={2000}
|
|
240
|
+
>
|
|
241
|
+
<App />
|
|
242
|
+
</HotkeyProvider>
|
|
243
|
+
~~~
|
|
244
|
+
|
|
245
|
+
- showFloatingButton: پیشفرض true.
|
|
246
|
+
- position: bottom-left (پیشفرض)، bottom-right، top-left یا top-right.
|
|
247
|
+
- offset: عدد یا { x, y } برحسب پیکسل.
|
|
248
|
+
- zIndex: پیشفرض 1000.
|
|
249
|
+
|
|
250
|
+
## شخصیسازی UI
|
|
251
|
+
|
|
252
|
+
رابط پکیج قابل تغییر است. ابتدا CSS پکیج و سپس stylesheet پروژهٔ خود را import کنید تا overrideها اعمال شوند.
|
|
253
|
+
|
|
254
|
+
~~~tsx
|
|
255
|
+
<HotkeyProvider actions={actions} className="my-product">
|
|
256
|
+
<HotkeySettingsTrigger className="my-shortcut-button">
|
|
257
|
+
Shortcuts
|
|
258
|
+
</HotkeySettingsTrigger>
|
|
259
|
+
<App />
|
|
260
|
+
</HotkeyProvider>
|
|
261
|
+
~~~
|
|
262
|
+
|
|
263
|
+
className روی Provider، wrapper محتوای شما و className روی HotkeySettingsTrigger، Trigger اختصاصی شماست. پنجرهٔ تنظیمات با portal در document.body رندر میشود؛ پس برای استایل آن از selectorهای پکیج استفاده کنید، نه selector وابسته به .my-product.
|
|
264
|
+
|
|
265
|
+
### مثال: رنگ و گردی اختصاصی
|
|
266
|
+
|
|
267
|
+
~~~css
|
|
268
|
+
/* app.css — بعد از hotkey-engine/styles.css import شود */
|
|
269
|
+
.hotkey-dialog[data-hotkey-theme="light"] {
|
|
270
|
+
--hk-header: linear-gradient(110deg, #075985, #0f766e);
|
|
271
|
+
--hk-text: #17324d;
|
|
272
|
+
--hk-muted: #52718f;
|
|
273
|
+
--hk-line: rgba(15, 118, 110, .18);
|
|
274
|
+
--hk-control: rgba(255, 255, 255, .72);
|
|
275
|
+
--hk-control-hover: rgba(236, 253, 245, .9);
|
|
276
|
+
border-radius: 24px;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.hotkey-floating-trigger {
|
|
280
|
+
background: #0f766e;
|
|
281
|
+
color: #fff;
|
|
282
|
+
border-color: transparent;
|
|
283
|
+
border-radius: 14px;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.hotkey-action-edit,
|
|
287
|
+
.hotkey-action-disable,
|
|
288
|
+
.hotkey-action-reset {
|
|
289
|
+
border-radius: 8px;
|
|
290
|
+
}
|
|
291
|
+
~~~
|
|
292
|
+
|
|
293
|
+
| selector | بخش |
|
|
294
|
+
| --- | --- |
|
|
295
|
+
| .hotkey-floating-trigger | دکمهٔ شناور |
|
|
296
|
+
| .hotkey-overlay | لایهٔ پشت پنجره |
|
|
297
|
+
| .hotkey-dialog | پنجرهٔ تنظیمات |
|
|
298
|
+
| .hotkey-glass-header | هدر |
|
|
299
|
+
| .hotkey-search-wrap، .hotkey-search | جستوجو |
|
|
300
|
+
| .hotkey-action-item | ردیف هر Action |
|
|
301
|
+
| .hotkey-action-edit، .hotkey-action-disable، .hotkey-action-reset | دکمههای Action |
|
|
302
|
+
| .hotkey-inline-feedback | خطای تداخل/رزرو |
|
|
303
|
+
| .hotkey-recorder | وضعیت ثبت میانبر |
|
|
304
|
+
|
|
305
|
+
متغیرهای اصلی CSS: --hk-header، --hk-panel، --hk-control، --hk-control-hover، --hk-text، --hk-muted و --hk-line. برای تم تیره، selector را به .hotkey-dialog[data-hotkey-theme="dark"] تغییر دهید.
|
|
306
|
+
|
|
307
|
+
## ذخیرهسازی و بازنشانی
|
|
308
|
+
|
|
309
|
+
تنظیمات کاربر پس از mount در localStorage با کلید نسخهدار hotkey-engine:v1 ذخیره میشوند. ویرایشها بلافاصله اجرا و ذخیره میشوند. آیکن بازنشانی هدر، همهٔ میانبرها را فوراً به حالت پیشفرض برمیگرداند.
|
|
310
|
+
|
|
311
|
+
Storage Adapter عمومی، همگامسازی چنددستگاهی و میانبرهای چندمرحلهای در این نسخه وجود ندارند.
|
|
312
|
+
|
|
313
|
+
## API Provider
|
|
314
|
+
|
|
315
|
+
| prop | پیشفرض | توضیح |
|
|
316
|
+
| --- | --- | --- |
|
|
317
|
+
| actions | — | آرایهٔ Actionها؛ الزامی. |
|
|
318
|
+
| showFloatingButton | true | نمایش دکمهٔ شناور. |
|
|
319
|
+
| position | bottom-left | محل دکمهٔ شناور. |
|
|
320
|
+
| offset | { x: 24, y: 24 } | فاصله از لبهها. |
|
|
321
|
+
| zIndex | 1000 | z-index دکمه. |
|
|
322
|
+
| theme | system | light، dark یا system. |
|
|
323
|
+
| locale | en | en یا fa. |
|
|
324
|
+
| dir | طبق locale | ltr یا rtl. |
|
|
325
|
+
| className | — | کلاس wrapper فرزندان Provider. |
|
|
326
|
+
| allowReservedShortcuts | false | اجازهٔ ثبت میانبرهای رزرو. |
|
|
327
|
+
|
|
328
|
+
## بررسی و توسعه
|
|
329
|
+
|
|
330
|
+
~~~bash
|
|
331
|
+
npm test
|
|
332
|
+
npm run build
|
|
333
|
+
npm run storybook
|
|
334
|
+
~~~
|
|
335
|
+
|
|
336
|
+
برای ساخت Storybook ایستا:
|
|
337
|
+
|
|
338
|
+
~~~bash
|
|
339
|
+
npm run build-storybook
|
|
340
|
+
~~~
|
|
341
|
+
|
|
342
|
+
## License
|
|
343
|
+
|
|
344
|
+
[MIT](./LICENSE)
|
|
Binary file
|
|
Binary file
|