@jjlmoya/utils-games-development 1.53.0 → 1.55.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/package.json +1 -1
- package/src/category/index.ts +2 -1
- package/src/entries.ts +6 -1
- package/src/index.ts +2 -0
- package/src/tests/contrast_legibility.test.ts +245 -0
- package/src/tests/locale_completeness.test.ts +2 -2
- package/src/tests/tool_validation.test.ts +2 -2
- package/src/tool/itchioGameTester/itchio-game-tester.css +3 -3
- package/src/tool/localizationSanitizer/localization-sanitizer.css +13 -13
- package/src/tool/retroSfxGenerator/bibliography.astro +6 -0
- package/src/tool/retroSfxGenerator/bibliography.ts +16 -0
- package/src/tool/retroSfxGenerator/client.ts +256 -0
- package/src/tool/retroSfxGenerator/component.astro +146 -0
- package/src/tool/retroSfxGenerator/entry.ts +27 -0
- package/src/tool/retroSfxGenerator/i18n/de.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/en.ts +224 -0
- package/src/tool/retroSfxGenerator/i18n/es.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/fr.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/id.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/it.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/ja.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/ko.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/nl.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/pl.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/pt.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/ru.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/sv.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/tr.ts +211 -0
- package/src/tool/retroSfxGenerator/i18n/zh.ts +211 -0
- package/src/tool/retroSfxGenerator/index.ts +11 -0
- package/src/tool/retroSfxGenerator/logic.test.ts +40 -0
- package/src/tool/retroSfxGenerator/logic.ts +214 -0
- package/src/tool/retroSfxGenerator/retro-sfx-generator.css +429 -0
- package/src/tool/retroSfxGenerator/seo.astro +15 -0
- package/src/tool/retroSfxGenerator/ui.ts +41 -0
- package/src/tool/saveFileEditor/game-save-file-editor.css +5 -5
- package/src/tool/spriteSheetPacker/sprite-sheet-packer.css +5 -5
- package/src/tool/steamBbcodeTranslator/app-client.ts +125 -0
- package/src/tool/steamBbcodeTranslator/bbcode-parser.ts +110 -0
- package/src/tool/steamBbcodeTranslator/bibliography.astro +6 -0
- package/src/tool/steamBbcodeTranslator/bibliography.ts +14 -0
- package/src/tool/steamBbcodeTranslator/component.astro +83 -0
- package/src/tool/steamBbcodeTranslator/entry.ts +28 -0
- package/src/tool/steamBbcodeTranslator/html-parser.ts +42 -0
- package/src/tool/steamBbcodeTranslator/html-renderer.ts +71 -0
- package/src/tool/steamBbcodeTranslator/i18n/de.ts +207 -0
- package/src/tool/steamBbcodeTranslator/i18n/en.ts +236 -0
- package/src/tool/steamBbcodeTranslator/i18n/es.ts +207 -0
- package/src/tool/steamBbcodeTranslator/i18n/fr.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/id.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/it.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/ja.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/ko.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/nl.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/pl.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/pt.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/ru.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/sv.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/tr.ts +195 -0
- package/src/tool/steamBbcodeTranslator/i18n/zh.ts +195 -0
- package/src/tool/steamBbcodeTranslator/index.ts +12 -0
- package/src/tool/steamBbcodeTranslator/logic.test.ts +41 -0
- package/src/tool/steamBbcodeTranslator/logic.ts +96 -0
- package/src/tool/steamBbcodeTranslator/markdown-parser.ts +115 -0
- package/src/tool/steamBbcodeTranslator/markdown-renderer.ts +85 -0
- package/src/tool/steamBbcodeTranslator/seo.astro +15 -0
- package/src/tool/steamBbcodeTranslator/steam-bbcode-translator.css +392 -0
- package/src/tool/steamBbcodeTranslator/storage.ts +30 -0
- package/src/tool/steamBbcodeTranslator/types.ts +36 -0
- package/src/tool/steamBbcodeTranslator/ui.ts +18 -0
- package/src/tool/steamCapsuleGenerator/steam-capsule-generator.css +6 -6
- package/src/tools.ts +4 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Node, TagNode } from './types';
|
|
2
|
+
|
|
3
|
+
function escapeMarkdown(value: string): string {
|
|
4
|
+
return value.replace(/([\\`*_{}[\]()#+!|>])/g, '\\$1');
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function escapeMarkdownUrl(value: string): string {
|
|
8
|
+
return value.replace(/[()\\ ]/g, (character) => `\\${character}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function cleanInline(value: string): string {
|
|
12
|
+
return value.replace(/[ \t]+\n/g, '\n').replace(/\n[ \t]+/g, '\n');
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function indent(value: string): string {
|
|
16
|
+
return value.split('\n').map((line) => line ? ` ${line}` : line).join('\n');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function inlineMarkdown(nodes: Node[]): string {
|
|
20
|
+
return cleanInline(nodes.map((node) => {
|
|
21
|
+
if (node.kind === 'text') return escapeMarkdown(node.value);
|
|
22
|
+
return renderMarkdownNode(node, true);
|
|
23
|
+
}).join(''));
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function renderMarkdownList(node: TagNode, ordered: boolean): string {
|
|
27
|
+
const items = node.children.filter((child): child is TagNode => child.kind === 'tag' && child.name === '*');
|
|
28
|
+
return items.map((item, index) => {
|
|
29
|
+
const content = item.children.map((child) => {
|
|
30
|
+
if (child.kind === 'tag' && (child.name === 'list' || child.name === 'olist')) {
|
|
31
|
+
return `\n${indent(renderMarkdownList(child, child.name === 'olist'))}`;
|
|
32
|
+
}
|
|
33
|
+
if (child.kind === 'text') return escapeMarkdown(child.value);
|
|
34
|
+
return renderMarkdownNode(child, true);
|
|
35
|
+
}).join('').trim();
|
|
36
|
+
return `${ordered ? `${index + 1}.` : '-'} ${content}`;
|
|
37
|
+
}).join('\n');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function renderCodeNode(node: TagNode, inline: boolean): string {
|
|
41
|
+
const inner = node.children.map((child) => child.kind === 'text' ? child.value : renderMarkdownNode(child, true)).join('');
|
|
42
|
+
return inline ? `\`${inner}\`` : `\`\`\`\n${inner}\n\`\`\`\n\n`;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function renderQuoteNode(node: TagNode): string {
|
|
46
|
+
return node.children.map((child) => inlineMarkdown([child])).join('').split('\n').map((line) => `> ${line}`).join('\n') + '\n\n';
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function renderMarkdownBasicInline(name: string, content: string): string | null {
|
|
50
|
+
if (name === 'b' || name === 'strong') return `**${content}**`;
|
|
51
|
+
if (name === 'i' || name === 'em') return `*${content}*`;
|
|
52
|
+
if (name === 'u') return `<u>${content}</u>`;
|
|
53
|
+
if (name === 's' || name === 'strike') return `~~${content}~~`;
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function renderMarkdownInlineNode(name: string, content: string, value?: string): string {
|
|
58
|
+
const basic = renderMarkdownBasicInline(name, content);
|
|
59
|
+
if (basic !== null) return basic;
|
|
60
|
+
if (name === 'url') return value ? `[${content || escapeMarkdown(value)}](${escapeMarkdownUrl(value)})` : content;
|
|
61
|
+
return content;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function renderMarkdownHeadingOrBlock(name: string, content: string, node: TagNode, inline: boolean): string | null {
|
|
65
|
+
if (name === 'h1') return `# ${content.trim()}\n\n`;
|
|
66
|
+
if (name === 'h2') return `## ${content.trim()}\n\n`;
|
|
67
|
+
if (name === 'h3') return `### ${content.trim()}\n\n`;
|
|
68
|
+
if (name === 'code') return renderCodeNode(node, inline);
|
|
69
|
+
if (name === 'quote') return renderQuoteNode(node);
|
|
70
|
+
if (name === 'spoiler') return `<details><summary>Spoiler</summary>${content}</details>`;
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function renderMarkdownNode(node: TagNode, inline = false): string {
|
|
75
|
+
const content = inlineMarkdown(node.children);
|
|
76
|
+
const headingOrBlock = renderMarkdownHeadingOrBlock(node.name, content, node, inline);
|
|
77
|
+
if (headingOrBlock !== null) return headingOrBlock;
|
|
78
|
+
if (node.name === 'list') return `${renderMarkdownList(node, false)}\n\n`;
|
|
79
|
+
if (node.name === 'olist') return `${renderMarkdownList(node, true)}\n\n`;
|
|
80
|
+
return renderMarkdownInlineNode(node.name, content, node.value);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function renderMarkdown(nodes: Node[]): string {
|
|
84
|
+
return nodes.map((node) => node.kind === 'text' ? escapeMarkdown(node.value) : renderMarkdownNode(node)).join('').replace(/\n{3,}/g, '\n\n').trim();
|
|
85
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
import { SEORenderer } from '@jjlmoya/utils-shared';
|
|
3
|
+
import { steamBbcodeTranslator } from './entry';
|
|
4
|
+
import type { KnownLocale } from '../../types';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: KnownLocale;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { locale = 'en' } = Astro.props;
|
|
11
|
+
const loader = steamBbcodeTranslator.i18n[locale] ?? steamBbcodeTranslator.i18n.en;
|
|
12
|
+
const content = loader ? await loader() : null;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
{content && content.seo && content.seo.length > 0 && <SEORenderer content={{ locale, sections: content.seo }} />}
|
|
@@ -0,0 +1,392 @@
|
|
|
1
|
+
.bbcode-root {
|
|
2
|
+
--bb-ink: #211e2d;
|
|
3
|
+
--bb-muted: #77728a;
|
|
4
|
+
--bb-line: #dcd8e7;
|
|
5
|
+
--bb-panel: #fffdfb;
|
|
6
|
+
--bb-soft: #f4f0f7;
|
|
7
|
+
--bb-accent: #724ee8;
|
|
8
|
+
--bb-accent-deep: #4c2fba;
|
|
9
|
+
--bb-cyan: #20a8b5;
|
|
10
|
+
--bb-warm: #ed8a52;
|
|
11
|
+
|
|
12
|
+
color: var(--bb-ink);
|
|
13
|
+
margin: 0 auto;
|
|
14
|
+
max-width: 1120px;
|
|
15
|
+
width: 100%;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
.theme-dark .bbcode-root {
|
|
19
|
+
--bb-ink: #f4f1fb;
|
|
20
|
+
--bb-muted: #aaa4bb;
|
|
21
|
+
--bb-line: #342e47;
|
|
22
|
+
--bb-panel: #171422;
|
|
23
|
+
--bb-soft: #211c30;
|
|
24
|
+
--bb-accent: #a68cff;
|
|
25
|
+
--bb-accent-deep: #c0b0ff;
|
|
26
|
+
--bb-cyan: #63d2dc;
|
|
27
|
+
--bb-warm: #f3a06d;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.bbcode-root * {
|
|
31
|
+
box-sizing: border-box;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.bbcode-root button,
|
|
35
|
+
.bbcode-root textarea {
|
|
36
|
+
font: inherit;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.bbcode-header,
|
|
40
|
+
.bbcode-input-card,
|
|
41
|
+
.bbcode-output-card,
|
|
42
|
+
.bbcode-preview-card {
|
|
43
|
+
background: var(--bb-panel);
|
|
44
|
+
border: 1px solid var(--bb-line);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
.bbcode-header {
|
|
48
|
+
align-items: center;
|
|
49
|
+
border-radius: 18px 18px 0 0;
|
|
50
|
+
display: flex;
|
|
51
|
+
justify-content: space-between;
|
|
52
|
+
gap: 1rem;
|
|
53
|
+
padding: 1rem 1.2rem;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.bbcode-title-row {
|
|
57
|
+
align-items: center;
|
|
58
|
+
display: flex;
|
|
59
|
+
gap: 0.75rem;
|
|
60
|
+
min-width: 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.bbcode-mark {
|
|
64
|
+
align-items: center;
|
|
65
|
+
background: var(--bb-soft);
|
|
66
|
+
border: 1px solid var(--bb-line);
|
|
67
|
+
border-radius: 8px;
|
|
68
|
+
color: var(--bb-accent);
|
|
69
|
+
display: inline-flex;
|
|
70
|
+
font-size: 0.9rem;
|
|
71
|
+
font-weight: 900;
|
|
72
|
+
height: 36px;
|
|
73
|
+
justify-content: center;
|
|
74
|
+
width: 58px;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.bbcode-kicker {
|
|
78
|
+
color: var(--bb-accent);
|
|
79
|
+
display: block;
|
|
80
|
+
font-size: 0.75rem;
|
|
81
|
+
font-weight: 900;
|
|
82
|
+
letter-spacing: 0.14em;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.bbcode-title-row strong,
|
|
86
|
+
.bbcode-draft-note {
|
|
87
|
+
color: var(--bb-muted);
|
|
88
|
+
display: block;
|
|
89
|
+
font-size: 0.75rem;
|
|
90
|
+
font-weight: 600;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.bbcode-draft-note {
|
|
94
|
+
text-align: right;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.bbcode-input-card {
|
|
98
|
+
border-top: 0;
|
|
99
|
+
padding: 1.2rem;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
.bbcode-section-head,
|
|
103
|
+
.bbcode-output-heading,
|
|
104
|
+
.bbcode-output-card-head,
|
|
105
|
+
.bbcode-input-footer {
|
|
106
|
+
align-items: flex-start;
|
|
107
|
+
display: flex;
|
|
108
|
+
justify-content: space-between;
|
|
109
|
+
gap: 1rem;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
.bbcode-section-head {
|
|
113
|
+
margin-bottom: 0.7rem;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
.bbcode-section-head h2,
|
|
117
|
+
.bbcode-output-heading h2 {
|
|
118
|
+
display: inline;
|
|
119
|
+
font-size: 1rem;
|
|
120
|
+
margin: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
.bbcode-step {
|
|
124
|
+
color: var(--bb-warm);
|
|
125
|
+
font-size: 0.75rem;
|
|
126
|
+
font-weight: 900;
|
|
127
|
+
margin-right: 0.45rem;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.bbcode-detected {
|
|
131
|
+
background: var(--bb-soft);
|
|
132
|
+
border-radius: 999px;
|
|
133
|
+
color: var(--bb-accent-deep);
|
|
134
|
+
font-size: 0.75rem;
|
|
135
|
+
padding: 0.3rem 0.55rem;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.bbcode-input-card textarea {
|
|
139
|
+
background: var(--bb-soft);
|
|
140
|
+
border: 1px solid var(--bb-line);
|
|
141
|
+
border-radius: 9px;
|
|
142
|
+
color: var(--bb-ink);
|
|
143
|
+
display: block;
|
|
144
|
+
font-size: 0.8rem;
|
|
145
|
+
line-height: 1.65;
|
|
146
|
+
min-height: 240px;
|
|
147
|
+
padding: 1rem;
|
|
148
|
+
resize: vertical;
|
|
149
|
+
width: 100%;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.bbcode-input-card textarea:focus {
|
|
153
|
+
border-color: var(--bb-accent);
|
|
154
|
+
box-shadow: 0 0 0 3px color-mix(in srgb, var(--bb-accent) 18%, transparent);
|
|
155
|
+
outline: 0;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.bbcode-input-footer {
|
|
159
|
+
align-items: center;
|
|
160
|
+
margin-top: 0.6rem;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
.bbcode-input-footer p {
|
|
164
|
+
color: var(--bb-muted);
|
|
165
|
+
font-size: 0.75rem;
|
|
166
|
+
line-height: 1.4;
|
|
167
|
+
margin: 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.bbcode-input-meta {
|
|
171
|
+
align-items: center;
|
|
172
|
+
color: var(--bb-muted);
|
|
173
|
+
display: flex;
|
|
174
|
+
flex-wrap: wrap;
|
|
175
|
+
font-size: 0.75rem;
|
|
176
|
+
gap: 0.8rem;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.bbcode-input-meta strong {
|
|
180
|
+
color: var(--bb-ink);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
.bbcode-clear-button,
|
|
184
|
+
.bbcode-icon-button {
|
|
185
|
+
background: transparent;
|
|
186
|
+
border: 1px solid var(--bb-line);
|
|
187
|
+
border-radius: 6px;
|
|
188
|
+
color: var(--bb-ink);
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
font-size: 0.75rem;
|
|
191
|
+
font-weight: 800;
|
|
192
|
+
padding: 0.38rem 0.6rem;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
.bbcode-clear-button:hover,
|
|
196
|
+
.bbcode-icon-button:hover {
|
|
197
|
+
border-color: var(--bb-accent);
|
|
198
|
+
color: var(--bb-accent-deep);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.bbcode-output-section {
|
|
202
|
+
background: var(--bb-soft);
|
|
203
|
+
border-left: 1px solid var(--bb-line);
|
|
204
|
+
border-right: 1px solid var(--bb-line);
|
|
205
|
+
padding: 1rem 1.2rem;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.bbcode-output-heading {
|
|
209
|
+
align-items: center;
|
|
210
|
+
margin-bottom: 0.7rem;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.bbcode-output-grid {
|
|
214
|
+
display: grid;
|
|
215
|
+
gap: 0.8rem;
|
|
216
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
.bbcode-output-card {
|
|
220
|
+
border-radius: 10px;
|
|
221
|
+
min-width: 0;
|
|
222
|
+
padding: 0.8rem;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
.bbcode-output-card-head {
|
|
226
|
+
align-items: center;
|
|
227
|
+
margin-bottom: 0.55rem;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
.bbcode-output-card h3 {
|
|
231
|
+
font-size: 0.78rem;
|
|
232
|
+
margin: 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
.bbcode-output-card pre {
|
|
236
|
+
background: var(--bb-soft);
|
|
237
|
+
border: 1px solid var(--bb-line);
|
|
238
|
+
border-radius: 7px;
|
|
239
|
+
color: var(--bb-ink);
|
|
240
|
+
font-size: 0.75rem;
|
|
241
|
+
line-height: 1.6;
|
|
242
|
+
margin: 0;
|
|
243
|
+
min-height: 180px;
|
|
244
|
+
max-height: 330px;
|
|
245
|
+
overflow: auto;
|
|
246
|
+
padding: 0.8rem;
|
|
247
|
+
white-space: pre-wrap;
|
|
248
|
+
overflow-wrap: anywhere;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
.bbcode-preview-card {
|
|
252
|
+
border-radius: 0 0 18px 18px;
|
|
253
|
+
border-top: 0;
|
|
254
|
+
padding: 1rem 1.2rem 1.2rem;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
.bbcode-preview-surface {
|
|
258
|
+
background: var(--bb-soft);
|
|
259
|
+
border: 1px solid var(--bb-line);
|
|
260
|
+
border-radius: 9px;
|
|
261
|
+
color: var(--bb-ink);
|
|
262
|
+
font-size: 0.82rem;
|
|
263
|
+
line-height: 1.6;
|
|
264
|
+
min-height: 110px;
|
|
265
|
+
padding: 1rem;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
.bbcode-preview-surface h1,
|
|
269
|
+
.bbcode-preview-surface h2,
|
|
270
|
+
.bbcode-preview-surface h3 {
|
|
271
|
+
line-height: 1.15;
|
|
272
|
+
margin: 0 0 0.6rem;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
.bbcode-preview-surface h1 {
|
|
276
|
+
font-size: 1.35rem;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
.bbcode-preview-surface h2 {
|
|
280
|
+
font-size: 1.12rem;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
.bbcode-preview-surface h3 {
|
|
284
|
+
font-size: 0.98rem;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
.bbcode-preview-surface p {
|
|
288
|
+
margin: 0.3rem 0;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
.bbcode-preview-surface ul,
|
|
292
|
+
.bbcode-preview-surface ol {
|
|
293
|
+
margin: 0.45rem 0;
|
|
294
|
+
padding-left: 1.3rem;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
.bbcode-preview-surface li {
|
|
298
|
+
margin: 0.15rem 0;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
.bbcode-preview-surface blockquote {
|
|
302
|
+
border-left: 3px solid var(--bb-accent);
|
|
303
|
+
color: var(--bb-muted);
|
|
304
|
+
margin: 0.6rem 0;
|
|
305
|
+
padding-left: 0.7rem;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.bbcode-preview-surface a {
|
|
309
|
+
color: var(--bb-accent-deep);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
.bbcode-preview-surface code,
|
|
313
|
+
.bbcode-preview-surface pre {
|
|
314
|
+
background: var(--bb-panel);
|
|
315
|
+
border: 1px solid var(--bb-line);
|
|
316
|
+
border-radius: 5px;
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.bbcode-preview-surface code {
|
|
320
|
+
padding: 0.1rem 0.25rem;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.bbcode-preview-surface pre {
|
|
324
|
+
overflow: auto;
|
|
325
|
+
padding: 0.6rem;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
.bbcode-preview-surface pre code {
|
|
329
|
+
border: 0;
|
|
330
|
+
padding: 0;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
.bbcode-preview-surface p:first-child:last-child {
|
|
334
|
+
color: var(--bb-muted);
|
|
335
|
+
margin: 0;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
.bbcode-sr-only {
|
|
339
|
+
height: 1px;
|
|
340
|
+
margin: -1px;
|
|
341
|
+
overflow: hidden;
|
|
342
|
+
position: absolute;
|
|
343
|
+
width: 1px;
|
|
344
|
+
clip-path: inset(50%);
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
.bbcode-root button:focus-visible,
|
|
348
|
+
.bbcode-root textarea:focus-visible {
|
|
349
|
+
outline: 3px solid color-mix(in srgb, var(--bb-cyan) 45%, transparent);
|
|
350
|
+
outline-offset: 2px;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
@media (max-width: 700px) {
|
|
354
|
+
.bbcode-header,
|
|
355
|
+
.bbcode-input-footer {
|
|
356
|
+
align-items: flex-start;
|
|
357
|
+
flex-direction: column;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
.bbcode-draft-note {
|
|
361
|
+
text-align: left;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
.bbcode-input-meta {
|
|
365
|
+
width: 100%;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
.bbcode-output-grid {
|
|
369
|
+
grid-template-columns: 1fr;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
@media (max-width: 460px) {
|
|
374
|
+
.bbcode-header,
|
|
375
|
+
.bbcode-input-card,
|
|
376
|
+
.bbcode-output-section,
|
|
377
|
+
.bbcode-preview-card {
|
|
378
|
+
padding-left: 0.85rem;
|
|
379
|
+
padding-right: 0.85rem;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
.bbcode-input-meta {
|
|
383
|
+
justify-content: space-between;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
@media (prefers-reduced-motion: reduce) {
|
|
388
|
+
.bbcode-root * {
|
|
389
|
+
scroll-behavior: auto;
|
|
390
|
+
transition-duration: 0.01ms;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { STORAGE_KEY } from './types';
|
|
2
|
+
|
|
3
|
+
export function loadSourceFromStorage(): string | null {
|
|
4
|
+
if (typeof window === 'undefined') return null;
|
|
5
|
+
try {
|
|
6
|
+
const value = window.localStorage.getItem(STORAGE_KEY);
|
|
7
|
+
return value?.trim() ? value : null;
|
|
8
|
+
} catch {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function saveSourceToStorage(source: string): void {
|
|
14
|
+
if (typeof window === 'undefined') return;
|
|
15
|
+
try {
|
|
16
|
+
if (source.trim()) window.localStorage.setItem(STORAGE_KEY, source);
|
|
17
|
+
else window.localStorage.removeItem(STORAGE_KEY);
|
|
18
|
+
} catch {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function clearSourceFromStorage(): void {
|
|
24
|
+
if (typeof window === 'undefined') return;
|
|
25
|
+
try {
|
|
26
|
+
window.localStorage.removeItem(STORAGE_KEY);
|
|
27
|
+
} catch {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export type OutputFormat = 'markdown' | 'html';
|
|
2
|
+
export type TranslationDirection = 'bbcode-to-output' | 'output-to-bbcode';
|
|
3
|
+
export type MarkupSyntax = 'bbcode' | 'markdown' | 'html';
|
|
4
|
+
|
|
5
|
+
export interface TranslationStats {
|
|
6
|
+
characters: number;
|
|
7
|
+
tags: number;
|
|
8
|
+
blocks: number;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TranslationResult {
|
|
12
|
+
output: string;
|
|
13
|
+
warnings: string[];
|
|
14
|
+
stats: TranslationStats;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface AutoTranslationResult {
|
|
18
|
+
detected: MarkupSyntax;
|
|
19
|
+
outputs: Partial<Record<MarkupSyntax, TranslationResult>>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface TextNode {
|
|
23
|
+
kind: 'text';
|
|
24
|
+
value: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface TagNode {
|
|
28
|
+
kind: 'tag';
|
|
29
|
+
name: string;
|
|
30
|
+
value?: string | undefined;
|
|
31
|
+
children: Node[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export type Node = TextNode | TagNode;
|
|
35
|
+
|
|
36
|
+
export const STORAGE_KEY = 'steam-bbcode-translator-draft-v1';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export interface SteamBbcodeTranslatorUI extends Record<string, string> {
|
|
2
|
+
editorLabel: string;
|
|
3
|
+
editorHint: string;
|
|
4
|
+
detectedLabel: string;
|
|
5
|
+
detectedEmpty: string;
|
|
6
|
+
bbcode: string;
|
|
7
|
+
markdown: string;
|
|
8
|
+
html: string;
|
|
9
|
+
copy: string;
|
|
10
|
+
copied: string;
|
|
11
|
+
clear: string;
|
|
12
|
+
characters: string;
|
|
13
|
+
blocks: string;
|
|
14
|
+
privacyNote: string;
|
|
15
|
+
persistenceNote: string;
|
|
16
|
+
previewLabel: string;
|
|
17
|
+
previewEmpty: string;
|
|
18
|
+
}
|
|
@@ -62,7 +62,7 @@
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
.steam-tab-btn.active {
|
|
65
|
-
color: var(--text-
|
|
65
|
+
color: var(--text-dark, #0f172a);
|
|
66
66
|
background: var(--accent-steam, #66c0f4);
|
|
67
67
|
box-shadow: 0 2px 8px var(--accent-shadow, rgba(102, 192, 244, 0.3));
|
|
68
68
|
}
|
|
@@ -298,7 +298,7 @@
|
|
|
298
298
|
border: 2px dashed var(--accent-safe, #ff4757);
|
|
299
299
|
background: var(--bg-safe, rgba(255, 71, 87, 0.15));
|
|
300
300
|
color: var(--text-light, #fff);
|
|
301
|
-
font-size: 0.
|
|
301
|
+
font-size: 0.75rem;
|
|
302
302
|
font-weight: 700;
|
|
303
303
|
padding: 0.25rem 0.5rem;
|
|
304
304
|
pointer-events: none;
|
|
@@ -322,7 +322,7 @@
|
|
|
322
322
|
|
|
323
323
|
.discount-tag {
|
|
324
324
|
background: var(--bg-discount, #4c6b22);
|
|
325
|
-
color: var(--accent-discount, #
|
|
325
|
+
color: var(--accent-discount, #fff);
|
|
326
326
|
font-size: 1.1rem;
|
|
327
327
|
font-weight: 800;
|
|
328
328
|
padding: 0.35rem 0.6rem;
|
|
@@ -336,7 +336,7 @@
|
|
|
336
336
|
}
|
|
337
337
|
|
|
338
338
|
.original-price {
|
|
339
|
-
font-size: 0.
|
|
339
|
+
font-size: 0.75rem;
|
|
340
340
|
text-decoration: line-through;
|
|
341
341
|
color: var(--text-muted-alt, #556772);
|
|
342
342
|
}
|
|
@@ -546,7 +546,7 @@
|
|
|
546
546
|
}
|
|
547
547
|
|
|
548
548
|
.stat-label {
|
|
549
|
-
font-size: 0.
|
|
549
|
+
font-size: 0.75rem;
|
|
550
550
|
color: var(--text-muted, #8b99a8);
|
|
551
551
|
text-transform: uppercase;
|
|
552
552
|
}
|
|
@@ -672,7 +672,7 @@
|
|
|
672
672
|
.steam-asset-badge {
|
|
673
673
|
background: var(--bg-topbar, #0d121a);
|
|
674
674
|
color: var(--accent-active, #67c1f5);
|
|
675
|
-
font-size: 0.
|
|
675
|
+
font-size: 0.75rem;
|
|
676
676
|
font-weight: 700;
|
|
677
677
|
padding: 0.15rem 0.4rem;
|
|
678
678
|
border-radius: 3px;
|
package/src/tools.ts
CHANGED
|
@@ -6,6 +6,8 @@ import { SPRITE_SHEET_PACKER_TOOL } from './tool/spriteSheetPacker';
|
|
|
6
6
|
import { AUDIO_LOOP_POINT_FINDER_TOOL } from './tool/audioLoopPointFinder';
|
|
7
7
|
import { SAVE_FILE_EDITOR_TOOL } from './tool/saveFileEditor';
|
|
8
8
|
import { LOCALIZATION_SANITIZER_TOOL } from './tool/localizationSanitizer';
|
|
9
|
+
import { STEAM_BBCODE_TRANSLATOR_TOOL } from './tool/steamBbcodeTranslator';
|
|
10
|
+
import { RETRO_SFX_GENERATOR_TOOL } from './tool/retroSfxGenerator';
|
|
9
11
|
|
|
10
12
|
export const ALL_TOOLS: ToolDefinition[] = [
|
|
11
13
|
STEAM_CAPSULE_GENERATOR_TOOL,
|
|
@@ -14,4 +16,6 @@ export const ALL_TOOLS: ToolDefinition[] = [
|
|
|
14
16
|
AUDIO_LOOP_POINT_FINDER_TOOL,
|
|
15
17
|
SAVE_FILE_EDITOR_TOOL,
|
|
16
18
|
LOCALIZATION_SANITIZER_TOOL,
|
|
19
|
+
STEAM_BBCODE_TRANSLATOR_TOOL,
|
|
20
|
+
RETRO_SFX_GENERATOR_TOOL,
|
|
17
21
|
];
|