-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathindex.html
More file actions
553 lines (540 loc) · 61.2 KB
/
Copy pathindex.html
File metadata and controls
553 lines (540 loc) · 61.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>devslab 오픈소스 — 라이브 데모 허브</title>
<meta
name="description"
content="devslab이 만드는 오픈소스 라이브러리들의 라이브 데모·문서 허브 — editor-ruler, ssrf-guard-js, numkey, kokey, vue-date-rail 그리고 Spring Boot 라이브러리."
/>
<meta name="google-site-verification" content="nIGsjwN9PU78ZpydYp5q4blPGCHBaZZRuvVI8wsJREg" />
<meta property="og:title" content="devslab 오픈소스 — 라이브 데모 허브" />
<meta
property="og:description"
content="프로덕션에서 먼저 검증하고 커뮤니티에 공개하는 라이브러리들 — 라이브 데모·문서·npm·Maven 링크 한 곳에."
/>
<meta property="og:url" content="https://devslab-kr.github.io/" />
<meta property="og:type" content="website" />
<meta property="og:site_name" content="devslab open source" />
<meta name="twitter:card" content="summary" />
<meta name="twitter:title" content="devslab 오픈소스 — 라이브 데모 허브" />
<meta
name="twitter:description"
content="프로덕션에서 먼저 검증하고 커뮤니티에 공개하는 라이브러리들 — 라이브 데모·문서·npm·Maven 링크 한 곳에."
/>
<link rel="canonical" href="https://devslab-kr.github.io/" />
<link rel="icon" href="https://devslab.kr/favicon.ico" />
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&family=Geist+Mono:wght@400;500&display=swap"
rel="stylesheet"
/>
<script>
// Match devslab.kr: light by default, persisted 'theme' key wins.
if (localStorage.getItem('theme') === 'dark') {
document.documentElement.classList.add('dark');
}
</script>
<style>
:root {
/* Tailwind zinc scale + electric cyan — same tokens as devslab.kr */
--bg: #fafafa; /* zinc-50 */
--panel: #ffffff;
--panel-soft: #fafafa; /* chip bg (zinc-50) */
--ink: #18181b; /* zinc-900 */
--body: #52525b; /* zinc-600 */
--muted: #71717a; /* zinc-500 */
--faint: #a1a1aa; /* zinc-400 */
--line: #e4e4e7; /* zinc-200 */
--line-strong: #d4d4d8; /* zinc-300 */
--electric: #0891b2; /* electric-600 */
--electric-strong: #0e7490; /* electric-700 */
--electric-raw: #06b6d4; /* electric-500 */
--dot: rgb(0 0 0 / 0.08);
--header-bg: rgb(255 255 255 / 0.85);
color-scheme: light;
}
html.dark {
--bg: #09090b; /* zinc-950 */
--panel: #18181b; /* zinc-900 */
--panel-soft: #09090b; /* zinc-950 */
--ink: #f4f4f5; /* zinc-100 */
--body: #a1a1aa; /* zinc-400 */
--muted: #71717a; /* zinc-500 */
--faint: #52525b; /* zinc-600 */
--line: #27272a; /* zinc-800 */
--line-strong: #3f3f46; /* zinc-700 */
--electric: #22d3ee; /* electric-400 */
--electric-strong: #67e8f9; /* electric-300 */
--dot: rgb(255 255 255 / 0.08);
--header-bg: rgb(9 9 11 / 0.85);
color-scheme: dark;
}
* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
body {
margin: 0;
background: var(--bg);
color: var(--ink);
font-family: 'Geist', -apple-system, 'Apple SD Gothic Neo', 'Pretendard', 'Noto Sans KR', sans-serif;
line-height: 1.65;
-webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility;
}
::selection { background: #06b6d4; color: #ffffff; }
html.dark ::selection { background: #22d3ee; color: #0a0a0a; }
.mono { font-family: 'Geist Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace; }
a { color: inherit; text-decoration: none; }
a:focus-visible, button:focus-visible {
outline: 2px solid var(--electric-raw);
outline-offset: 2px;
border-radius: 6px;
}
.wrap { max-width: 72rem; margin: 0 auto; padding: 0 1rem; }
@media (min-width: 640px) { .wrap { padding: 0 1.5rem; } }
/* ---------- Header ---------- */
header.top {
position: sticky; top: 0; z-index: 50;
border-bottom: 1px solid var(--line);
background: var(--header-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
}
.top-inner { display: flex; align-items: center; justify-content: space-between; padding: 1rem; }
@media (min-width: 640px) { .top-inner { padding: 1rem 1.5rem; } }
.brand { display: flex; align-items: center; gap: 0.625rem; }
.brand svg { width: 1.75rem; height: 1.75rem; color: var(--ink); flex: none; }
.brand .word { font-size: 1rem; font-weight: 600; letter-spacing: -0.025em; color: var(--ink); }
.brand .slash { font-size: 0.8125rem; color: var(--muted); }
.top-nav { display: flex; align-items: center; gap: 0.75rem; }
.top-nav a.nav-link {
font-family: 'Geist Mono', ui-monospace, monospace;
font-size: 0.875rem; color: var(--body);
transition: color 0.15s;
}
.top-nav a.nav-link:hover { color: var(--electric); }
@media (max-width: 519px) { .top-nav a.nav-link { display: none; } }
#theme-toggle {
display: inline-flex; align-items: center; justify-content: center;
width: 2.25rem; height: 2.25rem; border-radius: 0.375rem;
border: 1px solid var(--line-strong); background: var(--panel);
color: var(--body); cursor: pointer; transition: background 0.15s;
}
#theme-toggle:hover { background: var(--panel-soft); }
#theme-toggle svg { width: 1.25rem; height: 1.25rem; }
html:not(.dark) #icon-sun { display: none; }
html.dark #icon-moon { display: none; }
/* ---------- Language picker ---------- */
details.lang { position: relative; }
details.lang summary {
list-style: none; cursor: pointer;
display: inline-flex; align-items: center; gap: 0.375rem;
font-family: 'Geist Mono', ui-monospace, monospace;
font-size: 0.8125rem; color: var(--body);
height: 2.25rem; padding: 0 0.625rem;
border: 1px solid var(--line-strong); border-radius: 0.375rem;
background: var(--panel); transition: color 0.15s;
}
details.lang summary::-webkit-details-marker { display: none; }
details.lang summary:hover { color: var(--electric); }
details.lang .caret { font-size: 0.625rem; opacity: 0.7; }
details.lang .menu {
position: absolute; right: 0; top: calc(100% + 6px); z-index: 60;
min-width: 11rem; max-height: 60vh; overflow-y: auto;
background: var(--panel); border: 1px solid var(--line);
border-radius: 0.5rem; padding: 0.375rem;
box-shadow: 0 12px 32px rgb(0 0 0 / 0.16);
}
[dir="rtl"] details.lang .menu { right: auto; left: 0; }
details.lang .menu button {
display: block; width: 100%; text-align: start;
padding: 0.4rem 0.6rem; border: 0; background: none;
color: var(--ink); font-size: 0.8125rem; font-family: inherit;
border-radius: 0.375rem; cursor: pointer;
}
details.lang .menu button:hover { background: var(--panel-soft); color: var(--electric); }
details.lang .menu button.active { color: var(--electric); }
/* RTL (ar): the page mirrors, but code-flavored mono bits stay LTR. */
[dir="rtl"] .eyebrow, [dir="rtl"] .card-top, [dir="rtl"] .card h3,
[dir="rtl"] .links, [dir="rtl"] .brand, [dir="rtl"] .foot-links { direction: ltr; }
[dir="rtl"] .card-top { justify-content: space-between; }
/* ---------- Hero ---------- */
.hero { position: relative; overflow: hidden; border-bottom: 1px solid var(--line); }
.hero .dots {
pointer-events: none; position: absolute; inset: 0;
background-image: radial-gradient(circle, var(--dot) 1px, transparent 1px);
background-size: 24px 24px;
-webkit-mask-image: radial-gradient(ellipse at center, black 30%, transparent 75%);
mask-image: radial-gradient(ellipse at center, black 30%, transparent 75%);
}
.hero-inner { position: relative; padding-top: 5rem; padding-bottom: 5rem; }
@media (min-width: 768px) { .hero-inner { padding-top: 6.5rem; padding-bottom: 6.5rem; } }
.eyebrow {
font-family: 'Geist Mono', ui-monospace, monospace;
font-size: 0.875rem; color: var(--electric); margin: 0 0 1.5rem;
}
.cursor {
display: inline-block; width: 0.375rem; height: 0.875rem;
transform: translateY(1px); margin-left: 0.25rem; vertical-align: middle;
background: var(--electric-raw);
animation: blink 1.1s steps(2) infinite;
}
@keyframes blink { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
.hero h1 {
margin: 0 0 1.5rem; font-weight: 600; letter-spacing: -0.06em;
font-size: clamp(2.5rem, 6vw, 4.25rem); line-height: 1.05;
}
.hero h1 .accent { color: var(--electric); }
.hero .lede { max-width: 34em; margin: 0; font-size: 1.125rem; line-height: 1.75; color: var(--body); }
/* ---------- Sections ---------- */
section { padding: 3.5rem 0 1rem; }
section:last-of-type { padding-bottom: 4rem; }
.sec-head { margin-bottom: 2rem; }
.sec-head .eyebrow { margin-bottom: 0.75rem; }
.sec-head h2 {
margin: 0; font-size: 1.75rem; font-weight: 600; letter-spacing: -0.025em;
}
@media (min-width: 640px) { .sec-head h2 { font-size: 2rem; } }
.grid {
display: grid; gap: 1rem;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
}
.card {
display: flex; flex-direction: column;
border: 1px solid var(--line); border-radius: 0.75rem;
background: var(--panel); padding: 1.5rem;
transition: border-color 0.15s, box-shadow 0.15s;
}
.card:hover {
border-color: var(--electric-raw);
box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
}
html.dark .card:hover { border-color: #22d3ee; }
.card-top {
display: flex; align-items: center; justify-content: space-between;
margin-bottom: 1.25rem;
font-family: 'Geist Mono', ui-monospace, monospace;
font-size: 0.75rem;
}
.card-top .label { color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
.card-top .num { color: var(--faint); }
.card h3 {
margin: 0; font-size: 1.25rem; font-weight: 600; letter-spacing: -0.025em;
font-family: 'Geist Mono', ui-monospace, monospace;
}
.card p {
flex: 1 1 auto; margin: 0.75rem 0 0;
font-size: 0.875rem; line-height: 1.65; color: var(--body);
}
.card .links { display: flex; flex-wrap: wrap; gap: 0.375rem; margin-top: 1.25rem; }
.chip {
font-family: 'Geist Mono', ui-monospace, monospace;
font-size: 0.75rem; color: var(--body);
border: 1px solid var(--line-strong); border-radius: 0.375rem;
background: var(--panel-soft); padding: 0.25rem 0.625rem;
transition: color 0.15s, border-color 0.15s;
}
.chip:hover { color: var(--electric-strong); border-color: var(--electric-raw); }
.chip-primary { color: var(--electric); border-color: var(--electric-raw); }
/* ---------- Footer ---------- */
footer {
border-top: 1px solid var(--line);
background: var(--panel);
}
.foot-inner {
display: flex; flex-direction: column; align-items: flex-start;
justify-content: space-between; gap: 1rem;
padding: 2rem 1rem; font-size: 0.875rem;
}
@media (min-width: 640px) {
.foot-inner { flex-direction: row; align-items: center; padding: 2rem 1.5rem; }
}
.foot-brand { display: flex; align-items: center; gap: 0.75rem; }
.foot-brand svg { width: 1.25rem; height: 1.25rem; color: var(--ink); }
.foot-brand span {
font-family: 'Geist Mono', ui-monospace, monospace;
font-size: 0.75rem; color: var(--muted);
}
.foot-links { display: flex; flex-wrap: wrap; gap: 1.25rem; }
.foot-links a {
font-family: 'Geist Mono', ui-monospace, monospace;
font-size: 0.75rem; color: var(--body); transition: color 0.15s;
}
.foot-links a:hover { color: var(--electric); }
</style>
</head>
<body>
<header class="top">
<div class="wrap top-inner">
<a href="https://devslab.kr" class="brand" aria-label="DevsLab">
<svg viewBox="0 0 32 32" aria-hidden="true" fill="currentColor">
<path d="M 4 4 L 28 4 L 28 12 L 20 12 L 20 28 L 4 28 Z" />
</svg>
<span class="word">DevsLab</span>
<span class="slash mono">/ open-source</span>
</a>
<nav class="top-nav">
<a class="nav-link" href="https://github.com/devslab-kr">github</a>
<a class="nav-link" href="https://devslab.kr">devslab.kr</a>
<details class="lang" id="lang-picker">
<summary aria-label="Language"><span id="lang-current">KO</span><span class="caret" aria-hidden="true">▾</span></summary>
<div class="menu" role="menu">
<button type="button" data-lang="ko">한국어</button>
<button type="button" data-lang="en">English</button>
<button type="button" data-lang="ja">日本語</button>
<button type="button" data-lang="zh-HK">繁體中文 (香港)</button>
<button type="button" data-lang="zh-TW">繁體中文 (台灣)</button>
<button type="button" data-lang="hi">हिन्दी</button>
<button type="button" data-lang="vi">Tiếng Việt</button>
<button type="button" data-lang="id">Bahasa Indonesia</button>
<button type="button" data-lang="th">ภาษาไทย</button>
<button type="button" data-lang="pt-BR">Português (Brasil)</button>
<button type="button" data-lang="fr">Français</button>
<button type="button" data-lang="de">Deutsch</button>
<button type="button" data-lang="es">Español</button>
<button type="button" data-lang="ar" dir="rtl">العربية</button>
</div>
</details>
<button id="theme-toggle" type="button" aria-label="Toggle theme" title="Toggle theme">
<svg id="icon-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z" />
</svg>
<svg id="icon-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="4" />
<path d="M12 2v2m0 16v2M4.93 4.93l1.41 1.41m11.32 11.32 1.41 1.41M2 12h2m16 0h2M4.93 19.07l1.41-1.41M17.66 6.34l1.41-1.41" />
</svg>
</button>
</nav>
</div>
</header>
<main>
<section class="hero" style="padding: 0;">
<div class="dots" aria-hidden="true"></div>
<div class="wrap hero-inner">
<p class="eyebrow" dir="ltr">// open-source<span class="cursor"></span></p>
<h1>
<span id="t-h1a">devslab 오픈소스</span>
<br />
<span class="accent" id="t-h1b">라이브 데모 허브</span>
</h1>
<p class="lede" id="t-lede">
저희 SaaS 프로덕션에서 먼저 검증하고 커뮤니티에 공개하는
라이브러리들입니다. 라이브 데모와 문서, 설치 링크를 한곳에
모았습니다.
</p>
</div>
</section>
<div class="wrap">
<section aria-labelledby="sec-js">
<div class="sec-head">
<p class="eyebrow" dir="ltr">// npm @devslab</p>
<h2 id="sec-js">JavaScript / TypeScript</h2>
</div>
<div class="grid">
<article class="card">
<div class="card-top"><span class="label">// editor ui</span><span class="num">[01]</span></div>
<h3>editor-ruler</h3>
<p id="d-ruler">웹 리치텍스트 에디터를 위한 Word 스타일 가로 줄자 — 여백·들여쓰기 드래그 핸들, 세로 줄자, 가이드선+스냅. Froala · Tiptap · CKEditor 5 · Summernote 어댑터, API 1.0 안정.</p>
<div class="links">
<a class="chip chip-primary" href="/editor-ruler/" data-i18n="live">라이브 데모</a>
<a class="chip" href="https://github.com/devslab-kr/editor-ruler">GitHub</a>
<a class="chip" href="https://www.npmjs.com/package/@devslab/editor-ruler">npm</a>
</div>
</article>
<article class="card">
<div class="card-top"><span class="label">// security</span><span class="num">[02]</span></div>
<h3>ssrf-guard-js</h3>
<p id="d-ssrfjs">JS/TS SSRF 방어 — URL 정책 검증, 사설망 IP 차단, 리다이렉트 재검증, LLM 툴 입력 URL 가드. Node와 엣지 런타임 모두 지원.</p>
<div class="links">
<a class="chip chip-primary" href="/ssrf-guard-js/" data-i18n="docs">문서</a>
<a class="chip" href="https://github.com/devslab-kr/ssrf-guard-js">GitHub</a>
<a class="chip" href="https://www.npmjs.com/package/@devslab/ssrf-guard-js">npm</a>
</div>
</article>
<article class="card">
<div class="card-top"><span class="label">// input</span><span class="num">[03]</span></div>
<h3>numkey</h3>
<p id="d-numkey">"문자열인데 숫자"인 입력 필드의 모든 것 — 커서가 튀지 않는 천 단위 콤마, 한글 금액 병기(150만), 만/억 축약 입력. 금액 안전 문자열 모델.</p>
<div class="links">
<a class="chip chip-primary" href="/numkey/" data-i18n="live">라이브 데모</a>
<a class="chip" href="https://github.com/devslab-kr/numkey">GitHub</a>
<a class="chip" href="https://www.npmjs.com/package/@devslab/numkey">npm</a>
</div>
</article>
<article class="card">
<div class="card-top"><span class="label">// input</span><span class="num">[04]</span></div>
<h3>kokey</h3>
<p id="d-kokey">키보드 배열 오타 복원기 — 한글 두벌식 IME 오토마톤 내장, 러시아어·히브리어·태국어 등 7개 배열, 스크립트 자동 감지.</p>
<div class="links">
<a class="chip chip-primary" href="/kokey/" data-i18n="live">라이브 데모</a>
<a class="chip" href="https://github.com/devslab-kr/kokey">GitHub</a>
<a class="chip" href="https://www.npmjs.com/package/@devslab/kokey">npm</a>
</div>
</article>
<article class="card">
<div class="card-top"><span class="label">// vue ui</span><span class="num">[05]</span></div>
<h3>vue-date-rail</h3>
<p id="d-rail">Vue 3용 가로 무한 스크롤 날짜 레일 피커 — 배송·예약 앱의 캘린더 스트립 패턴. headless 코어 + Intl 기반 i18n.</p>
<div class="links">
<a class="chip chip-primary" href="/vue-date-rail/" data-i18n="live">라이브 데모</a>
<a class="chip" href="https://github.com/devslab-kr/vue-date-rail">GitHub</a>
<a class="chip" href="https://www.npmjs.com/package/@devslab/vue-date-rail">npm</a>
</div>
</article>
</div>
</section>
<section aria-labelledby="sec-jvm">
<div class="sec-head">
<p class="eyebrow" dir="ltr">// maven central kr.devslab</p>
<h2 id="sec-jvm">JVM / Spring Boot</h2>
</div>
<div class="grid">
<article class="card">
<div class="card-top"><span class="label">// pagination</span><span class="num">[06]</span></div>
<h3>easy-paging</h3>
<p id="d-paging">어노테이션 기반 페이지네이션 — 오프셋(@AutoPaginate)과 키셋(@KeysetPaginate)을 한 스타터로. Spring Boot 4 활성 / 3.x 유지보수 라인.</p>
<div class="links">
<a class="chip chip-primary" href="https://easy-paging.devslab.kr" data-i18n="docs">문서</a>
<a class="chip" href="https://github.com/devslab-kr/easy-paging-spring-boot-starter">GitHub</a>
<a class="chip" href="https://central.sonatype.com/artifact/kr.devslab/easy-paging-spring-boot-starter">Maven</a>
</div>
</article>
<article class="card">
<div class="card-top"><span class="label">// security</span><span class="num">[07]</span></div>
<h3>ssrf-guard</h3>
<p id="d-ssrf">JVM SSRF 방어 — 9개 HTTP 클라이언트 모듈 + Spring AI/LangChain4j LLM 에이전트 툴 URL 검증.</p>
<div class="links">
<a class="chip" href="https://github.com/devslab-kr/ssrf-guard">GitHub</a>
<a class="chip" href="https://central.sonatype.com/artifact/kr.devslab/ssrf-guard">Maven</a>
</div>
</article>
<article class="card">
<div class="card-top"><span class="label">// logging</span><span class="num">[08]</span></div>
<h3>api-log</h3>
<p id="d-apilog">이벤트 기반 API 로깅, PostgreSQL JSONB 저장 — core + JPA/R2DBC/MyBatis 드라이버.</p>
<div class="links">
<a class="chip" href="https://github.com/devslab-kr/api-log">GitHub</a>
<a class="chip" href="https://central.sonatype.com/artifact/kr.devslab/api-log-core">Maven</a>
</div>
</article>
<article class="card">
<div class="card-top"><span class="label">// platform starter</span><span class="num">[09]</span></div>
<h3>devslab-kit</h3>
<p id="d-kit">Spring Boot 4 플랫폼 스타터 — 인증, RBAC+ABAC, 멀티테넌시, 동적 메뉴, 감사 로깅을 자동 구성으로.</p>
<div class="links">
<a class="chip chip-primary" href="https://devslab-kit.devslab.kr" data-i18n="docs">문서</a>
<a class="chip" href="https://github.com/devslab-kr/devslab-kit">GitHub</a>
</div>
</article>
<article class="card">
<div class="card-top"><span class="label">// data migration</span><span class="num">[10]</span></div>
<h3>datalinq</h3>
<p id="d-datalinq">컨벤션 기반 크로스 벤더 JDBC 데이터 마이그레이션 TUI — sql/ 폴더를 넣으면 메뉴가 생깁니다. jbang으로 설치.</p>
<div class="links">
<a class="chip" href="https://github.com/devslab-kr/datalinq">GitHub</a>
<a class="chip" href="https://github.com/devslab-kr/datalinq/releases/latest">Releases</a>
</div>
</article>
<article class="card">
<div class="card-top"><span class="label">// demos</span><span class="num">[11]</span></div>
<h3>devslab-examples</h3>
<p id="d-examples">위 라이브러리들의 실행 가능한 Spring Boot 데모 19종 — clone, bootRun, curl. 커뮤니티 Discussions도 여기서.</p>
<div class="links">
<a class="chip" href="https://github.com/devslab-kr/devslab-examples">GitHub</a>
<a class="chip" href="https://github.com/devslab-kr/devslab-examples/discussions">Discussions</a>
</div>
</article>
</div>
</section>
</div>
</main>
<footer>
<div class="wrap foot-inner">
<div class="foot-brand">
<svg viewBox="0 0 32 32" aria-hidden="true" fill="currentColor">
<path d="M 4 4 L 28 4 L 28 12 L 20 12 L 20 28 L 4 28 Z" />
</svg>
<span id="copyright">© 2026 DevsLab · Built in Seoul</span>
</div>
<div class="foot-links" dir="ltr">
<a href="https://devslab.kr">devslab.kr</a>
<a href="https://github.com/devslab-kr">github.com/devslab.kr</a>
</div>
</div>
</footer>
<script>
// ---- i18n: 14 locales, same set as devslab.kr ----------------------
(function () {
var I18N = {"ko": {"h1a": "devslab 오픈소스", "h1b": "라이브 데모 허브", "lede": "저희 SaaS 프로덕션에서 먼저 검증하고 커뮤니티에 공개하는 라이브러리들입니다. 라이브 데모와 문서, 설치 링크를 한곳에 모았습니다.", "live": "라이브 데모", "docs": "문서", "cards": {"ruler": "웹 리치텍스트 에디터를 위한 Word 스타일 가로 줄자 — 여백·들여쓰기 드래그 핸들, 세로 줄자, 가이드선+스냅. Froala · Tiptap · CKEditor 5 · Summernote 어댑터, API 1.0 안정.", "ssrfjs": "JS/TS SSRF 방어 — URL 정책 검증, 사설망 IP 차단, 리다이렉트 재검증, LLM 툴 입력 URL 가드. Node와 엣지 런타임 모두 지원.", "numkey": "\"문자열인데 숫자\"인 입력 필드의 모든 것 — 커서가 튀지 않는 천 단위 콤마, 한글 금액 병기(150만), 만/억 축약 입력. 금액 안전 문자열 모델.", "kokey": "키보드 배열 오타 복원기 — 한글 두벌식 IME 오토마톤 내장, 러시아어·히브리어·태국어 등 7개 배열, 스크립트 자동 감지.", "rail": "Vue 3용 가로 무한 스크롤 날짜 레일 피커 — 배송·예약 앱의 캘린더 스트립 패턴. headless 코어 + Intl 기반 i18n.", "paging": "어노테이션 기반 페이지네이션 — 오프셋(@AutoPaginate)과 키셋(@KeysetPaginate)을 한 스타터로. Spring Boot 4 활성 / 3.x 유지보수 라인.", "ssrf": "JVM SSRF 방어 — 9개 HTTP 클라이언트 모듈 + Spring AI/LangChain4j LLM 에이전트 툴 URL 검증.", "apilog": "이벤트 기반 API 로깅, PostgreSQL JSONB 저장 — core + JPA/R2DBC/MyBatis 드라이버.", "kit": "Spring Boot 4 플랫폼 스타터 — 인증, RBAC+ABAC, 멀티테넌시, 동적 메뉴, 감사 로깅을 자동 구성으로.", "datalinq": "컨벤션 기반 크로스 벤더 JDBC 데이터 마이그레이션 TUI — sql/ 폴더를 넣으면 메뉴가 생깁니다. jbang으로 설치.", "examples": "위 라이브러리들의 실행 가능한 Spring Boot 데모 19종 — clone, bootRun, curl. 커뮤니티 Discussions도 여기서."}}, "en": {"h1a": "devslab open source", "h1b": "live demo hub", "lede": "Libraries proven in our SaaS production first, then released to the community. Live demos, docs and install links, all in one place.", "live": "Live demo", "docs": "Docs", "cards": {"ruler": "Word-style horizontal ruler for web rich-text editors — margin & indent drag handles, a vertical ruler, guides with snapping. Froala · Tiptap · CKEditor 5 · Summernote adapters, API stable at 1.0.", "ssrfjs": "SSRF defense for JS/TS — URL policy validation, private-network IP blocking, redirect re-validation, LLM tool-input URL guard. Works on Node and edge runtimes.", "numkey": "Everything for \"numeric but string\" input fields — thousands commas without cursor jumps, Korean amount hints (150만), 만/억 shorthand entry. A money-safe string model.", "kokey": "Wrong-keyboard-layout typo restorer — built-in Korean Dubeolsik IME automaton, 7 layouts incl. Russian, Hebrew and Thai, automatic script detection.", "rail": "Horizontally infinite-scrolling date rail picker for Vue 3 — the calendar-strip pattern of delivery and booking apps. Headless core + Intl-based i18n.", "paging": "Annotation-driven pagination — offset (@AutoPaginate) and keyset (@KeysetPaginate) in one starter. Spring Boot 4 active / 3.x maintenance line.", "ssrf": "SSRF defense for the JVM — 9 HTTP client modules plus Spring AI / LangChain4j LLM agent tool-URL validation.", "apilog": "Event-driven API logging into PostgreSQL JSONB — core plus JPA / R2DBC / MyBatis drivers.", "kit": "Spring Boot 4 platform starter — auth, RBAC+ABAC, multi-tenancy, dynamic menus and audit logging via auto-configuration.", "datalinq": "Convention-based cross-vendor JDBC data-migration TUI — drop in an sql/ folder and the menu appears. Installs via jbang.", "examples": "19 runnable Spring Boot demos of the libraries above — clone, bootRun, curl. Community Discussions live here too."}}, "ja": {"h1a": "devslabオープンソース", "h1b": "ライブデモハブ", "lede": "自社SaaSの本番環境で先に検証し、コミュニティに公開しているライブラリ群です。ライブデモ・ドキュメント・インストールリンクをここに集約しました。", "live": "ライブデモ", "docs": "ドキュメント", "cards": {"ruler": "WebリッチテキストエディタのためのWord風水平ルーラー — 余白・インデントのドラッグハンドル、垂直ルーラー、ガイド線+スナップ。Froala · Tiptap · CKEditor 5 · Summernote アダプター、APIは1.0で安定。", "ssrfjs": "JS/TS向けSSRF防御 — URLポリシー検証、プライベートIP遮断、リダイレクト再検証、LLMツール入力のURLガード。Nodeとエッジ両ランタイム対応。", "numkey": "「文字列だが数値」な入力欄のすべて — カーソルが飛ばない桁区切りカンマ、韓国語金額の併記(150만)、만/억の短縮入力。金額安全な文字列モデル。", "kokey": "キーボード配列の打ち間違い復元 — 韓国語2ボル式IMEオートマトン内蔵、ロシア語・ヘブライ語・タイ語など7配列、スクリプト自動判定。", "rail": "Vue 3向け横無限スクロールの日付レールピッカー — 配送・予約アプリのカレンダーストリップパターン。ヘッドレスコア + IntlベースのI18n。", "paging": "アノテーション駆動のページネーション — オフセット(@AutoPaginate)とキーセット(@KeysetPaginate)を1つのスターターで。Spring Boot 4 アクティブ / 3.x メンテナンスライン。", "ssrf": "JVM向けSSRF防御 — 9つのHTTPクライアントモジュール + Spring AI / LangChain4j LLMエージェントのツールURL検証。", "apilog": "イベント駆動のAPIロギング、PostgreSQL JSONBへ保存 — core + JPA / R2DBC / MyBatisドライバー。", "kit": "Spring Boot 4プラットフォームスターター — 認証、RBAC+ABAC、マルチテナンシー、動的メニュー、監査ログを自動構成で。", "datalinq": "規約ベースのクロスベンダーJDBCデータ移行TUI — sql/フォルダを置くだけでメニューが現れる。jbangでインストール。", "examples": "上記ライブラリの実行可能なSpring Bootデモ19種 — clone、bootRun、curl。コミュニティDiscussionsもここで。"}}, "zh-HK": {"h1a": "devslab 開源", "h1b": "即時示範中心", "lede": "先在我們的 SaaS 正式環境驗證、再開源給社群的函式庫。即時示範、文件與安裝連結都集中在這裡。", "live": "即時示範", "docs": "文件", "cards": {"ruler": "為網頁富文字編輯器打造的 Word 風格水平標尺 — 邊界/縮排拖曳手柄、垂直標尺、輔助線+吸附。Froala · Tiptap · CKEditor 5 · Summernote 配接器,API 1.0 穩定版。", "ssrfjs": "JS/TS 的 SSRF 防禦 — URL 政策驗證、私有網段 IP 封鎖、重新導向再驗證、LLM 工具輸入 URL 防護。支援 Node 與邊緣執行環境。", "numkey": "「字串但其實是數字」輸入欄位的一切 — 千分位逗號且游標不跳動、韓文金額提示(150만)、만/억 縮寫輸入。金額安全的字串模型。", "kokey": "鍵盤配置打錯字還原器 — 內建韓文兩拍式 IME 自動機,支援俄文、希伯來文、泰文等 7 種配置,自動偵測文字系統。", "rail": "Vue 3 的水平無限捲動日期軌道選擇器 — 外送/預約 App 常見的日曆條模式。Headless 核心 + 基於 Intl 的 i18n。", "paging": "以註解驅動的分頁 — offset(@AutoPaginate)與 keyset(@KeysetPaginate)合於一個 starter。Spring Boot 4 主線 / 3.x 維護線。", "ssrf": "JVM 的 SSRF 防禦 — 9 個 HTTP 客戶端模組,外加 Spring AI / LangChain4j LLM 代理工具 URL 驗證。", "apilog": "事件驅動的 API 日誌,寫入 PostgreSQL JSONB — core 加上 JPA / R2DBC / MyBatis 驅動。", "kit": "Spring Boot 4 平台 starter — 認證、RBAC+ABAC、多租戶、動態選單與稽核日誌,全部自動組態。", "datalinq": "以慣例為本的跨供應商 JDBC 資料遷移 TUI — 放入 sql/ 資料夾選單就出現。以 jbang 安裝。", "examples": "上述函式庫的 19 個可執行 Spring Boot 範例 — clone、bootRun、curl。社群 Discussions 也在這裡。"}}, "zh-TW": {"h1a": "devslab 開源", "h1b": "即時示範中心", "lede": "先在我們的 SaaS 正式環境驗證、再開源給社群的函式庫。即時示範、文件與安裝連結都集中在這裡。", "live": "即時示範", "docs": "文件", "cards": {"ruler": "為網頁富文字編輯器打造的 Word 風格水平標尺 — 邊界/縮排拖曳手柄、垂直標尺、輔助線+吸附。Froala · Tiptap · CKEditor 5 · Summernote 配接器,API 1.0 穩定版。", "ssrfjs": "JS/TS 的 SSRF 防禦 — URL 政策驗證、私有網段 IP 封鎖、重新導向再驗證、LLM 工具輸入 URL 防護。支援 Node 與邊緣執行環境。", "numkey": "「字串但其實是數字」輸入欄位的一切 — 千分位逗號且游標不跳動、韓文金額提示(150만)、만/억 縮寫輸入。金額安全的字串模型。", "kokey": "鍵盤配置打錯字還原器 — 內建韓文兩拍式 IME 自動機,支援俄文、希伯來文、泰文等 7 種配置,自動偵測文字系統。", "rail": "Vue 3 的水平無限捲動日期軌道選擇器 — 外送/預約 App 常見的日曆條模式。Headless 核心 + 基於 Intl 的 i18n。", "paging": "以註解驅動的分頁 — offset(@AutoPaginate)與 keyset(@KeysetPaginate)合於一個 starter。Spring Boot 4 主線 / 3.x 維護線。", "ssrf": "JVM 的 SSRF 防禦 — 9 個 HTTP 客戶端模組,外加 Spring AI / LangChain4j LLM 代理工具 URL 驗證。", "apilog": "事件驅動的 API 日誌,寫入 PostgreSQL JSONB — core 加上 JPA / R2DBC / MyBatis 驅動。", "kit": "Spring Boot 4 平台 starter — 認證、RBAC+ABAC、多租戶、動態選單與稽核日誌,全部自動組態。", "datalinq": "以慣例為本的跨供應商 JDBC 資料遷移 TUI — 放入 sql/ 資料夾選單就出現。以 jbang 安裝。", "examples": "上述函式庫的 19 個可執行 Spring Boot 範例 — clone、bootRun、curl。社群 Discussions 也在這裡。"}}, "hi": {"h1a": "devslab ओपन सोर्स", "h1b": "लाइव डेमो हब", "lede": "पहले हमारे SaaS प्रोडक्शन में परखी गईं, फिर कम्युनिटी के लिए जारी लाइब्रेरियाँ। लाइव डेमो, डॉक्स और इंस्टॉल लिंक — सब एक जगह।", "live": "लाइव डेमो", "docs": "डॉक्स", "cards": {"ruler": "वेब रिच-टेक्स्ट एडिटर के लिए Word-शैली क्षैतिज रूलर — मार्जिन/इंडेंट ड्रैग हैंडल, वर्टिकल रूलर, स्नैप वाली गाइड लाइनें। Froala · Tiptap · CKEditor 5 · Summernote अडैप्टर, API 1.0 पर स्थिर।", "ssrfjs": "JS/TS के लिए SSRF सुरक्षा — URL नीति सत्यापन, निजी नेटवर्क IP ब्लॉकिंग, रीडायरेक्ट पुनर्सत्यापन, LLM टूल-इनपुट URL गार्ड। Node और edge दोनों रनटाइम पर।", "numkey": "\"स्ट्रिंग मगर संख्या\" इनपुट फ़ील्ड के लिए सब कुछ — बिना कर्सर उछले हज़ार-विभाजक कॉमा, कोरियाई राशि संकेत (150만), 만/억 शॉर्टहैंड। पैसे के लिए सुरक्षित स्ट्रिंग मॉडल।", "kokey": "गलत कीबोर्ड लेआउट टाइपो रिस्टोरर — बिल्ट-इन कोरियाई Dubeolsik IME ऑटोमेटन, रूसी/हिब्रू/थाई समेत 7 लेआउट, स्क्रिप्ट की स्वतः पहचान।", "rail": "Vue 3 के लिए क्षैतिज अनंत-स्क्रॉल डेट रेल पिकर — डिलीवरी/बुकिंग ऐप्स का कैलेंडर-स्ट्रिप पैटर्न। हेडलेस कोर + Intl-आधारित i18n।", "paging": "एनोटेशन-चालित पेजिनेशन — offset (@AutoPaginate) और keyset (@KeysetPaginate) एक ही स्टार्टर में। Spring Boot 4 सक्रिय / 3.x रखरखाव लाइन।", "ssrf": "JVM के लिए SSRF सुरक्षा — 9 HTTP क्लाइंट मॉड्यूल + Spring AI / LangChain4j LLM एजेंट टूल-URL सत्यापन।", "apilog": "इवेंट-आधारित API लॉगिंग, PostgreSQL JSONB में — core + JPA / R2DBC / MyBatis ड्राइवर।", "kit": "Spring Boot 4 प्लेटफ़ॉर्म स्टार्टर — ऑथ, RBAC+ABAC, मल्टी-टेनेंसी, डायनामिक मेन्यू और ऑडिट लॉगिंग, ऑटो-कॉन्फ़िगरेशन से।", "datalinq": "कन्वेंशन-आधारित क्रॉस-वेंडर JDBC डेटा-माइग्रेशन TUI — sql/ फ़ोल्डर डालें और मेन्यू बन जाता है। jbang से इंस्टॉल।", "examples": "ऊपर की लाइब्रेरियों के 19 चलने योग्य Spring Boot डेमो — clone, bootRun, curl। कम्युनिटी Discussions भी यहीं।"}}, "vi": {"h1a": "mã nguồn mở devslab", "h1b": "trung tâm demo trực tiếp", "lede": "Các thư viện được kiểm chứng trong môi trường SaaS production của chúng tôi trước khi mở cho cộng đồng. Demo trực tiếp, tài liệu và liên kết cài đặt — tất cả ở một nơi.", "live": "Demo trực tiếp", "docs": "Tài liệu", "cards": {"ruler": "Thước ngang kiểu Word cho trình soạn thảo rich-text trên web — tay kéo lề/thụt lề, thước dọc, đường gióng có bắt dính. Adapter cho Froala · Tiptap · CKEditor 5 · Summernote, API ổn định từ 1.0.", "ssrfjs": "Phòng chống SSRF cho JS/TS — kiểm tra chính sách URL, chặn IP mạng nội bộ, kiểm tra lại khi redirect, bảo vệ URL đầu vào tool LLM. Chạy trên Node và edge runtime.", "numkey": "Mọi thứ cho ô nhập \"chuỗi nhưng là số\" — dấu phẩy hàng nghìn không nhảy con trỏ, chú thích số tiền tiếng Hàn (150만), nhập tắt 만/억. Mô hình chuỗi an toàn cho tiền tệ.", "kokey": "Khôi phục gõ nhầm bố cục bàn phím — tích hợp automaton IME Dubeolsik tiếng Hàn, 7 bố cục gồm Nga, Hebrew, Thái, tự nhận diện hệ chữ.", "rail": "Bộ chọn ngày dạng thanh cuộn ngang vô hạn cho Vue 3 — mẫu dải lịch của app giao hàng/đặt chỗ. Lõi headless + i18n dựa trên Intl.", "paging": "Phân trang bằng annotation — offset (@AutoPaginate) và keyset (@KeysetPaginate) trong một starter. Spring Boot 4 chính / 3.x bảo trì.", "ssrf": "Phòng chống SSRF cho JVM — 9 module HTTP client cùng kiểm tra URL tool của agent LLM Spring AI / LangChain4j.", "apilog": "Ghi log API hướng sự kiện vào PostgreSQL JSONB — core cùng driver JPA / R2DBC / MyBatis.", "kit": "Starter nền tảng Spring Boot 4 — xác thực, RBAC+ABAC, đa tenant, menu động và audit log qua auto-configuration.", "datalinq": "TUI di trú dữ liệu JDBC đa nhà cung cấp theo quy ước — bỏ thư mục sql/ vào là menu xuất hiện. Cài bằng jbang.", "examples": "19 bản demo Spring Boot chạy được của các thư viện trên — clone, bootRun, curl. Discussions cộng đồng cũng ở đây."}}, "id": {"h1a": "open source devslab", "h1b": "hub demo langsung", "lede": "Pustaka yang diuji dulu di produksi SaaS kami, lalu dirilis ke komunitas. Demo langsung, dokumentasi, dan tautan pemasangan — semuanya di satu tempat.", "live": "Demo langsung", "docs": "Dokumentasi", "cards": {"ruler": "Penggaris horizontal gaya Word untuk editor rich-text web — pegangan seret margin/indentasi, penggaris vertikal, garis bantu dengan snap. Adapter Froala · Tiptap · CKEditor 5 · Summernote, API stabil di 1.0.", "ssrfjs": "Pertahanan SSRF untuk JS/TS — validasi kebijakan URL, pemblokiran IP jaringan privat, validasi ulang redirect, penjaga URL input tool LLM. Berjalan di Node dan edge runtime.", "numkey": "Segalanya untuk field input \"string tapi angka\" — koma ribuan tanpa kursor melompat, keterangan nominal Korea (150만), input singkat 만/억. Model string yang aman untuk uang.", "kokey": "Pemulih salah ketik akibat layout keyboard — automaton IME Dubeolsik Korea bawaan, 7 layout termasuk Rusia, Ibrani, Thai, deteksi aksara otomatis.", "rail": "Date rail picker dengan scroll horizontal tak berujung untuk Vue 3 — pola strip kalender di aplikasi pengiriman/reservasi. Inti headless + i18n berbasis Intl.", "paging": "Paginasi berbasis anotasi — offset (@AutoPaginate) dan keyset (@KeysetPaginate) dalam satu starter. Spring Boot 4 aktif / jalur pemeliharaan 3.x.", "ssrf": "Pertahanan SSRF untuk JVM — 9 modul klien HTTP plus validasi URL tool agen LLM Spring AI / LangChain4j.", "apilog": "Logging API berbasis event ke PostgreSQL JSONB — core plus driver JPA / R2DBC / MyBatis.", "kit": "Starter platform Spring Boot 4 — auth, RBAC+ABAC, multi-tenancy, menu dinamis, dan audit logging lewat auto-configuration.", "datalinq": "TUI migrasi data JDBC lintas vendor berbasis konvensi — taruh folder sql/ dan menunya muncul. Instal lewat jbang.", "examples": "19 demo Spring Boot siap jalan dari pustaka di atas — clone, bootRun, curl. Discussions komunitas juga di sini."}}, "th": {"h1a": "โอเพนซอร์ส devslab", "h1b": "ฮับเดโมสด", "lede": "ไลบรารีที่ผ่านการพิสูจน์ในโปรดักชัน SaaS ของเราก่อน แล้วจึงเปิดสู่คอมมูนิตี้ เดโมสด เอกสาร และลิงก์ติดตั้ง รวมไว้ในที่เดียว", "live": "เดโมสด", "docs": "เอกสาร", "cards": {"ruler": "ไม้บรรทัดแนวนอนสไตล์ Word สำหรับ rich-text editor บนเว็บ — จุดลากขอบ/ย่อหน้า ไม้บรรทัดแนวตั้ง เส้นไกด์พร้อมสแนป อะแดปเตอร์ Froala · Tiptap · CKEditor 5 · Summernote API เสถียรที่ 1.0", "ssrfjs": "การป้องกัน SSRF สำหรับ JS/TS — ตรวจนโยบาย URL บล็อก IP เครือข่ายภายใน ตรวจซ้ำเมื่อ redirect และการ์ด URL อินพุตของ LLM tool รองรับทั้ง Node และ edge runtime", "numkey": "ทุกอย่างสำหรับช่องกรอก \"สตริงแต่เป็นตัวเลข\" — คอมมาหลักพันโดยเคอร์เซอร์ไม่กระโดด คำบอกจำนวนเงินเกาหลี (150만) และการพิมพ์ย่อ 만/억 โมเดลสตริงที่ปลอดภัยต่อจำนวนเงิน", "kokey": "ตัวกู้คืนการพิมพ์ผิดเลย์เอาต์คีย์บอร์ด — มีออโตมาตอน IME ดูบ็อลชิกเกาหลีในตัว รองรับ 7 เลย์เอาต์รวมถึงรัสเซีย ฮีบรู ไทย พร้อมตรวจสคริปต์อัตโนมัติ", "rail": "ตัวเลือกวันที่แบบรางเลื่อนแนวนอนไม่รู้จบสำหรับ Vue 3 — แพตเทิร์นแถบปฏิทินของแอปจัดส่ง/จองคิว คอร์แบบ headless + i18n บนฐาน Intl", "paging": "การแบ่งหน้าแบบใช้แอนโนเทชัน — offset (@AutoPaginate) และ keyset (@KeysetPaginate) ในสตาร์เตอร์เดียว Spring Boot 4 สายหลัก / 3.x สายบำรุงรักษา", "ssrf": "การป้องกัน SSRF สำหรับ JVM — โมดูล HTTP client 9 ตัว พร้อมตรวจ URL ของ tool เอเจนต์ LLM ใน Spring AI / LangChain4j", "apilog": "การเก็บล็อก API แบบอิงอีเวนต์ลง PostgreSQL JSONB — core พร้อมไดรเวอร์ JPA / R2DBC / MyBatis", "kit": "สตาร์เตอร์แพลตฟอร์ม Spring Boot 4 — การยืนยันตัวตน RBAC+ABAC มัลติเทแนนซี เมนูไดนามิก และล็อกตรวจสอบ ด้วย auto-configuration", "datalinq": "TUI ย้ายข้อมูล JDBC ข้ามผู้ผลิตแบบอิงคอนเวนชัน — วางโฟลเดอร์ sql/ แล้วเมนูจะปรากฏ ติดตั้งด้วย jbang", "examples": "เดโม Spring Boot รันได้จริง 19 ชุดของไลบรารีด้านบน — clone, bootRun, curl รวมถึง Discussions ของคอมมูนิตี้"}}, "pt-BR": {"h1a": "open source devslab", "h1b": "hub de demos ao vivo", "lede": "Bibliotecas comprovadas primeiro na nossa produção SaaS e então liberadas para a comunidade. Demos ao vivo, docs e links de instalação, tudo em um só lugar.", "live": "Demo ao vivo", "docs": "Docs", "cards": {"ruler": "Régua horizontal no estilo Word para editores rich-text na web — alças de arrastar para margens e recuo, régua vertical, guias com snap. Adaptadores Froala · Tiptap · CKEditor 5 · Summernote, API estável na 1.0.", "ssrfjs": "Defesa SSRF para JS/TS — validação de política de URL, bloqueio de IPs de rede privada, revalidação de redirects, guarda de URL em inputs de tools LLM. Funciona em Node e runtimes edge.", "numkey": "Tudo para campos \"string, mas numéricos\" — separador de milhar sem saltos de cursor, indicação de valores em coreano (150만), atalhos 만/억. Um modelo de string seguro para dinheiro.", "kokey": "Restaurador de digitação no layout errado — autômato IME Dubeolsik coreano embutido, 7 layouts incl. russo, hebraico e tailandês, detecção automática de escrita.", "rail": "Seletor de datas em trilho com rolagem horizontal infinita para Vue 3 — o padrão de faixa de calendário de apps de entrega e reservas. Núcleo headless + i18n baseado em Intl.", "paging": "Paginação por anotações — offset (@AutoPaginate) e keyset (@KeysetPaginate) em um único starter. Spring Boot 4 ativo / linha de manutenção 3.x.", "ssrf": "Defesa SSRF para a JVM — 9 módulos de clientes HTTP mais validação de URLs de tools de agentes LLM (Spring AI / LangChain4j).", "apilog": "Logging de API orientado a eventos em PostgreSQL JSONB — core mais drivers JPA / R2DBC / MyBatis.", "kit": "Starter de plataforma Spring Boot 4 — autenticação, RBAC+ABAC, multi-tenancy, menus dinâmicos e audit logging via auto-configuração.", "datalinq": "TUI de migração de dados JDBC multi-fornecedor por convenção — coloque uma pasta sql/ e o menu aparece. Instala via jbang.", "examples": "19 demos Spring Boot executáveis das bibliotecas acima — clone, bootRun, curl. As Discussions da comunidade também moram aqui."}}, "fr": {"h1a": "open source devslab", "h1b": "hub de démos live", "lede": "Des bibliothèques d'abord éprouvées dans notre production SaaS, puis ouvertes à la communauté. Démos live, docs et liens d'installation, réunis au même endroit.", "live": "Démo live", "docs": "Docs", "cards": {"ruler": "Règle horizontale façon Word pour les éditeurs rich-text web — poignées de marge et de retrait, règle verticale, guides avec magnétisme. Adaptateurs Froala · Tiptap · CKEditor 5 · Summernote, API stable en 1.0.", "ssrfjs": "Défense SSRF pour JS/TS — validation de politique d'URL, blocage des IP privées, revalidation des redirections, garde des URL en entrée d'outils LLM. Compatible Node et runtimes edge.", "numkey": "Tout pour les champs « chaîne mais numérique » — séparateurs de milliers sans saut de curseur, rappel des montants en coréen (150만), saisie abrégée 만/억. Un modèle de chaîne sûr pour les montants.", "kokey": "Restaurateur de frappes au mauvais agencement clavier — automate IME coréen Dubeolsik intégré, 7 agencements dont russe, hébreu, thaï, détection automatique de l'écriture.", "rail": "Sélecteur de dates en rail à défilement horizontal infini pour Vue 3 — le motif « bande calendrier » des apps de livraison et réservation. Cœur headless + i18n basé sur Intl.", "paging": "Pagination pilotée par annotations — offset (@AutoPaginate) et keyset (@KeysetPaginate) dans un seul starter. Spring Boot 4 actif / ligne de maintenance 3.x.", "ssrf": "Défense SSRF pour la JVM — 9 modules clients HTTP plus la validation des URL d'outils d'agents LLM (Spring AI / LangChain4j).", "apilog": "Journalisation d'API pilotée par événements vers PostgreSQL JSONB — core plus pilotes JPA / R2DBC / MyBatis.", "kit": "Starter de plateforme Spring Boot 4 — authentification, RBAC+ABAC, multi-tenant, menus dynamiques et journal d'audit via l'auto-configuration.", "datalinq": "TUI de migration de données JDBC multi-fournisseurs par convention — déposez un dossier sql/ et le menu apparaît. Installation via jbang.", "examples": "19 démos Spring Boot exécutables des bibliothèques ci-dessus — clone, bootRun, curl. Les Discussions de la communauté sont ici aussi."}}, "de": {"h1a": "devslab Open Source", "h1b": "Live-Demo-Hub", "lede": "Bibliotheken, zuerst in unserer SaaS-Produktion bewährt, dann für die Community veröffentlicht. Live-Demos, Doku und Install-Links an einem Ort.", "live": "Live-Demo", "docs": "Doku", "cards": {"ruler": "Word-artiges horizontales Lineal für Web-Rich-Text-Editoren — Zieh-Griffe für Ränder und Einzug, vertikales Lineal, Hilfslinien mit Einrasten. Adapter für Froala · Tiptap · CKEditor 5 · Summernote, API stabil ab 1.0.", "ssrfjs": "SSRF-Schutz für JS/TS — URL-Policy-Validierung, Blockieren privater Netz-IPs, Redirect-Revalidierung, URL-Guard für LLM-Tool-Eingaben. Läuft auf Node und Edge-Runtimes.", "numkey": "Alles für \"String, aber Zahl\"-Eingabefelder — Tausendertrennzeichen ohne Cursorsprünge, koreanische Betragsanzeige (150만), 만/억-Kurzformen. Ein geldsicheres String-Modell.", "kokey": "Wiederhersteller für Tipper im falschen Tastaturlayout — eingebauter koreanischer Dubeolsik-IME-Automat, 7 Layouts u. a. Russisch, Hebräisch, Thai, automatische Schrifterkennung.", "rail": "Horizontal endlos scrollender Datums-Rail-Picker für Vue 3 — das Kalenderstreifen-Muster von Liefer- und Buchungs-Apps. Headless-Kern + Intl-basierte i18n.", "paging": "Annotationsgesteuerte Pagination — Offset (@AutoPaginate) und Keyset (@KeysetPaginate) in einem Starter. Spring Boot 4 aktiv / 3.x-Wartungslinie.", "ssrf": "SSRF-Schutz für die JVM — 9 HTTP-Client-Module plus Tool-URL-Validierung für LLM-Agenten (Spring AI / LangChain4j).", "apilog": "Ereignisgesteuertes API-Logging nach PostgreSQL JSONB — Core plus JPA-/R2DBC-/MyBatis-Treiber.", "kit": "Spring-Boot-4-Plattform-Starter — Auth, RBAC+ABAC, Mandantenfähigkeit, dynamische Menüs und Audit-Logging per Auto-Konfiguration.", "datalinq": "Konventionsbasierte herstellerübergreifende JDBC-Datenmigrations-TUI — sql/-Ordner ablegen, Menü erscheint. Installation per jbang.", "examples": "19 lauffähige Spring-Boot-Demos der obigen Bibliotheken — clone, bootRun, curl. Auch die Community-Discussions wohnen hier."}}, "es": {"h1a": "open source devslab", "h1b": "hub de demos en vivo", "lede": "Bibliotecas probadas primero en nuestra producción SaaS y luego liberadas a la comunidad. Demos en vivo, docs y enlaces de instalación, todo en un solo lugar.", "live": "Demo en vivo", "docs": "Docs", "cards": {"ruler": "Regla horizontal estilo Word para editores rich-text web — tiradores de margen y sangría, regla vertical, guías con imán. Adaptadores para Froala · Tiptap · CKEditor 5 · Summernote, API estable en 1.0.", "ssrfjs": "Defensa SSRF para JS/TS — validación de políticas de URL, bloqueo de IPs de red privada, revalidación de redirecciones, guarda de URL en entradas de tools LLM. Funciona en Node y runtimes edge.", "numkey": "Todo para campos \"string pero numéricos\" — comas de miles sin saltos de cursor, indicación de importes en coreano (150만), entrada abreviada 만/억. Un modelo de string seguro para dinero.", "kokey": "Restaurador de errores por teclado equivocado — autómata IME coreano Dubeolsik incorporado, 7 distribuciones incl. ruso, hebreo y tailandés, detección automática de escritura.", "rail": "Selector de fechas en riel con scroll horizontal infinito para Vue 3 — el patrón de tira de calendario de apps de entrega y reservas. Núcleo headless + i18n basada en Intl.", "paging": "Paginación por anotaciones — offset (@AutoPaginate) y keyset (@KeysetPaginate) en un solo starter. Spring Boot 4 activo / línea de mantenimiento 3.x.", "ssrf": "Defensa SSRF para la JVM — 9 módulos de clientes HTTP más validación de URLs de tools de agentes LLM (Spring AI / LangChain4j).", "apilog": "Registro de API orientado a eventos en PostgreSQL JSONB — core más drivers JPA / R2DBC / MyBatis.", "kit": "Starter de plataforma Spring Boot 4 — autenticación, RBAC+ABAC, multitenencia, menús dinámicos y audit logging vía autoconfiguración.", "datalinq": "TUI de migración de datos JDBC multiproveedor por convención — coloca una carpeta sql/ y aparece el menú. Se instala con jbang.", "examples": "19 demos Spring Boot ejecutables de las bibliotecas de arriba — clone, bootRun, curl. Las Discussions de la comunidad también viven aquí."}}, "ar": {"h1a": "devslab مفتوح المصدر", "h1b": "مركز العروض الحية", "lede": "مكتبات أثبتت نفسها أولاً في بيئة إنتاج SaaS لدينا ثم أُتيحت للمجتمع. العروض الحية والوثائق وروابط التثبيت في مكان واحد.", "live": "عرض حي", "docs": "الوثائق", "cards": {"ruler": "مسطرة أفقية بأسلوب Word لمحرّرات النصوص الغنية على الويب — مقابض سحب للهوامش والمسافة البادئة، مسطرة رأسية، وخطوط إرشاد مع التقاط. محوّلات Froala · Tiptap · CKEditor 5 · Summernote، وواجهة برمجية مستقرة عند 1.0.", "ssrfjs": "دفاع SSRF لِـ JS/TS — التحقق من سياسات URL، حظر عناوين الشبكات الخاصة، إعادة التحقق عند إعادة التوجيه، وحماية روابط مدخلات أدوات LLM. يعمل على Node وبيئات الحافة.", "numkey": "كل ما تحتاجه حقول الإدخال \"نصية لكنها أرقام\" — فواصل الآلاف دون قفز المؤشر، وإظهار المبلغ بالكورية (150만)، وإدخال مختصر 만/억. نموذج نصي آمن للمبالغ.", "kokey": "مستعيد الأخطاء الناتجة عن تخطيط لوحة مفاتيح خاطئ — أوتوماتون IME كوري (دوبولسيك) مدمج، و7 تخطيطات منها الروسية والعبرية والتايلاندية، مع كشف تلقائي لنظام الكتابة.", "rail": "منتقي تواريخ بشريط تمرير أفقي لا نهائي لـ Vue 3 — نمط شريط التقويم في تطبيقات التوصيل والحجز. نواة headless مع i18n قائم على Intl.", "paging": "ترقيم صفحات قائم على التوصيفات — offset (@AutoPaginate) وkeyset (@KeysetPaginate) في حزمة بادئة واحدة. خط Spring Boot 4 نشط / 3.x للصيانة.", "ssrf": "دفاع SSRF لبيئة JVM — 9 وحدات لعملاء HTTP مع التحقق من روابط أدوات وكلاء LLM (Spring AI / LangChain4j).", "apilog": "تسجيل استدعاءات API قائم على الأحداث في PostgreSQL JSONB — النواة مع مشغّلات JPA / R2DBC / MyBatis.", "kit": "حزمة بادئة لمنصة Spring Boot 4 — مصادقة وRBAC+ABAC وتعدد المستأجرين وقوائم ديناميكية وسجل تدقيق عبر التهيئة التلقائية.", "datalinq": "واجهة نصية (TUI) لترحيل بيانات JDBC عبر عدة قواعد بيانات وفق الاصطلاحات — ضع مجلد sql/ فتظهر القائمة. تثبيت عبر jbang.", "examples": "19 عرضًا تجريبيًا قابلاً للتشغيل بـ Spring Boot للمكتبات أعلاه — clone وbootRun وcurl. ونقاشات المجتمع هنا أيضًا."}}};
var RTL = { ar: true };
var picker = document.getElementById('lang-picker');
function detect() {
try {
var saved = localStorage.getItem('hub-lang');
if (saved && I18N[saved]) return saved;
} catch (e) {}
var langs = navigator.languages || [navigator.language || 'en'];
for (var i = 0; i < langs.length; i++) {
var l = (langs[i] || '').toLowerCase();
if (!l) continue;
for (var k in I18N) if (k.toLowerCase() === l) return k;
var base = l.split('-')[0];
if (base === 'zh') return l.indexOf('hk') > -1 ? 'zh-HK' : 'zh-TW';
if (base === 'pt') return 'pt-BR';
if (I18N[base]) return base;
}
return 'en';
}
function setAll(sel, text) {
var nodes = document.querySelectorAll(sel);
for (var i = 0; i < nodes.length; i++) nodes[i].textContent = text;
}
function apply(lang, persist) {
var t = I18N[lang];
if (!t) return;
document.documentElement.lang = lang;
document.documentElement.dir = RTL[lang] ? 'rtl' : 'ltr';
document.title = t.h1a + ' — ' + t.h1b;
document.getElementById('t-h1a').textContent = t.h1a;
document.getElementById('t-h1b').textContent = t.h1b;
document.getElementById('t-lede').textContent = t.lede;
setAll('[data-i18n="live"]', t.live);
setAll('[data-i18n="docs"]', t.docs);
for (var key in t.cards) {
var el = document.getElementById('d-' + key);
if (el) el.textContent = t.cards[key];
}
document.getElementById('lang-current').textContent = lang.toUpperCase();
var buttons = picker.querySelectorAll('.menu button');
for (var i = 0; i < buttons.length; i++) {
buttons[i].classList.toggle('active', buttons[i].getAttribute('data-lang') === lang);
}
if (persist) { try { localStorage.setItem('hub-lang', lang); } catch (e) {} }
}
picker.addEventListener('click', function (event) {
var btn = event.target.closest('button[data-lang]');
if (!btn) return;
apply(btn.getAttribute('data-lang'), true);
picker.open = false;
});
document.addEventListener('click', function (event) {
if (picker.open && !picker.contains(event.target)) picker.open = false;
});
document.addEventListener('keydown', function (event) {
if (event.key === 'Escape') picker.open = false;
});
apply(detect(), false);
})();
document.getElementById('theme-toggle').addEventListener('click', function () {
var dark = document.documentElement.classList.toggle('dark');
localStorage.setItem('theme', dark ? 'dark' : 'light');
});
document.getElementById('copyright').textContent =
'© ' + new Date().getFullYear() + ' DevsLab · Built in Seoul';
</script>
</body>
</html>