hamzus-ui 0.0.244 → 0.0.246

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hamzus-ui",
3
- "version": "0.0.244",
3
+ "version": "0.0.246",
4
4
  "type": "module",
5
5
  "main": "index.js",
6
6
  "svelte": "index.js",
@@ -3,45 +3,28 @@
3
3
  import IconButton from '..//IconButton/IconButton.svelte';
4
4
 
5
5
  // ---------- État ----------
6
- export let initialValue = `# Mon Projet
7
-
8
- > Une courte description qui donne envie de scroller.
9
-
10
- ## Installation
11
-
12
- \`\`\`bash
13
- npm install mon-projet
14
- \`\`\`
15
-
16
- ## Utilisation
17
-
18
- \`\`\`js
19
- import { run } from 'mon-projet'
20
- run()
21
- \`\`\`
22
-
23
- - [x] Setup du repo
24
- - [ ] Écrire les tests
25
- - [ ] Publier sur npm
26
-
27
- Voir la [documentation](https://example.com) pour plus de détails.
28
- `;
29
-
30
- let source = initialValue;
6
+ export let value = ``;
7
+ export let onChange = ()=>{};
8
+ let previousValue = value;
31
9
  let mode = 'split'; // 'edit' | 'split' | 'preview'
32
10
  let filename = 'README.md';
33
11
  let dirty = false;
34
12
  let textareaEl;
35
13
  let copied = false;
36
14
 
37
- $: wordCount = (source.match(/\S+/g) || []).length;
38
- $: charCount = source.length;
15
+ $: wordCount = (value.match(/\S+/g) || []).length;
16
+ $: charCount = value.length;
39
17
  $: readMinutes = Math.max(1, Math.round(wordCount / 200));
40
- $: html = renderMarkdown(source);
18
+ $: html = renderMarkdown(value);
41
19
 
42
20
  function onInput(e) {
43
- source = e.target.value;
21
+ value = e.target.value;
44
22
  dirty = true;
23
+ if (previousValue !== value) {
24
+ onChange(value)
25
+ }
26
+
27
+ previousValue = value
45
28
  }
46
29
 
47
30
  // ---------- Petit moteur markdown (sans dépendance) ----------
@@ -186,9 +169,9 @@ Voir la [documentation](https://example.com) pour plus de détails.
186
169
  if (!textareaEl) return;
187
170
  const start = textareaEl.selectionStart;
188
171
  const end = textareaEl.selectionEnd;
189
- const selected = source.slice(start, end) || placeholder;
190
- const next = source.slice(0, start) + before + selected + after + source.slice(end);
191
- source = next;
172
+ const selected = value.slice(start, end) || placeholder;
173
+ const next = value.slice(0, start) + before + selected + after + value.slice(end);
174
+ value = next;
192
175
  dirty = true;
193
176
  requestAnimationFrame(() => {
194
177
  textareaEl.focus();
@@ -200,8 +183,8 @@ Voir la [documentation](https://example.com) pour plus de détails.
200
183
  function insertLinePrefix(prefix) {
201
184
  if (!textareaEl) return;
202
185
  const start = textareaEl.selectionStart;
203
- const lineStart = source.lastIndexOf('\n', start - 1) + 1;
204
- source = source.slice(0, lineStart) + prefix + source.slice(lineStart);
186
+ const lineStart = value.lastIndexOf('\n', start - 1) + 1;
187
+ value = value.slice(0, lineStart) + prefix + value.slice(lineStart);
205
188
  dirty = true;
206
189
  requestAnimationFrame(() => textareaEl.focus());
207
190
  }
@@ -220,7 +203,7 @@ Voir la [documentation](https://example.com) pour plus de détails.
220
203
 
221
204
  async function copyMarkdown() {
222
205
  try {
223
- await navigator.clipboard.writeText(source);
206
+ await navigator.clipboard.writeText(value);
224
207
  copied = true;
225
208
  setTimeout(() => (copied = false), 1600);
226
209
  } catch (e) {
@@ -229,7 +212,7 @@ Voir la [documentation](https://example.com) pour plus de détails.
229
212
  }
230
213
 
231
214
  function downloadFile() {
232
- const blob = new Blob([source], { type: 'text/markdown;charset=utf-8' });
215
+ const blob = new Blob([value], { type: 'text/markdown;charset=utf-8' });
233
216
  const url = URL.createObjectURL(blob);
234
217
  const a = document.createElement('a');
235
218
  a.href = url;
@@ -262,7 +245,7 @@ Voir la [documentation](https://example.com) pour plus de détails.
262
245
  bind:this={textareaEl}
263
246
  class="editor-pane"
264
247
  spellcheck="false"
265
- value={source}
248
+ value={value}
266
249
  on:input={onInput}
267
250
  placeholder="Écris ton README en Markdown..."
268
251
  ></textarea>
@@ -305,8 +288,6 @@ Voir la [documentation](https://example.com) pour plus de détails.
305
288
  .readme-editor {
306
289
  display: flex;
307
290
  flex-direction: column;
308
- height: 640px;
309
- max-height: 90vh;
310
291
  background: var(--bg);
311
292
  color: var(--text);
312
293
  border: 1px solid var(--border);
@@ -191,8 +191,6 @@ Voir la [documentation](https://example.com) pour plus de détails.
191
191
  .readme-preview {
192
192
  display: flex;
193
193
  flex-direction: column;
194
- height: 640px;
195
- max-height: 90vh;
196
194
  background: var(--bg);
197
195
  color: var(--text);
198
196
  border: 1px solid var(--border);
@@ -1,383 +0,0 @@
1
- <script>
2
- // ---------- État ----------
3
- export let initialValue = `# Mon Projet
4
-
5
- > Une courte description qui donne envie de scroller.
6
-
7
- ## Installation
8
-
9
- \`\`\`bash
10
- npm install mon-projet
11
- \`\`\`
12
-
13
- ## Utilisation
14
-
15
- \`\`\`js
16
- import { run } from 'mon-projet'
17
- run()
18
- \`\`\`
19
-
20
- - [x] Setup du repo
21
- - [ ] Écrire les tests
22
- - [ ] Publier sur npm
23
-
24
- Voir la [documentation](https://example.com) pour plus de détails.
25
- `;
26
- export let filename = 'README.md';
27
-
28
- let source = initialValue;
29
-
30
- $: html = renderMarkdown(source);
31
-
32
- // ---------- Petit moteur markdown (sans dépendance) ----------
33
- function escapeHtml(str) {
34
- return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
35
- }
36
-
37
- function renderInline(text) {
38
- let t = escapeHtml(text);
39
- t = t.replace(/`([^`]+)`/g, '<code>$1</code>');
40
- t = t.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '<img alt="$1" src="$2" />');
41
- t = t.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');
42
- t = t.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
43
- t = t.replace(/(^|[^*])\*([^*]+)\*(?!\*)/g, '$1<em>$2</em>');
44
- t = t.replace(/~~([^~]+)~~/g, '<del>$1</del>');
45
- return t;
46
- }
47
-
48
- function renderMarkdown(md) {
49
- const lines = md.replace(/\r\n/g, '\n').split('\n');
50
- let out = [];
51
- let i = 0;
52
- let inCodeBlock = false;
53
- let codeLang = '';
54
- let codeBuf = [];
55
- let listBuf = [];
56
- let listType = null;
57
-
58
- function flushList() {
59
- if (listBuf.length) {
60
- const tag = listType === 'ol' ? 'ol' : 'ul';
61
- out.push(`<${tag}>${listBuf.join('')}</${tag}>`);
62
- listBuf = [];
63
- listType = null;
64
- }
65
- }
66
-
67
- while (i < lines.length) {
68
- const line = lines[i];
69
- const fence = line.match(/^```(.*)$/);
70
-
71
- if (fence) {
72
- if (!inCodeBlock) {
73
- inCodeBlock = true;
74
- codeLang = fence[1].trim();
75
- codeBuf = [];
76
- } else {
77
- inCodeBlock = false;
78
- out.push(
79
- `<pre class="code-block" data-lang="${escapeHtml(codeLang || 'text')}"><code>${escapeHtml(codeBuf.join('\n'))}</code></pre>`
80
- );
81
- }
82
- i++;
83
- continue;
84
- }
85
- if (inCodeBlock) {
86
- codeBuf.push(line);
87
- i++;
88
- continue;
89
- }
90
-
91
- const heading = line.match(/^(#{1,6})\s+(.*)$/);
92
- if (heading) {
93
- flushList();
94
- const level = heading[1].length;
95
- out.push(`<h${level}>${renderInline(heading[2])}</h${level}>`);
96
- i++;
97
- continue;
98
- }
99
-
100
- const quote = line.match(/^>\s?(.*)$/);
101
- if (quote) {
102
- flushList();
103
- out.push(`<blockquote>${renderInline(quote[1])}</blockquote>`);
104
- i++;
105
- continue;
106
- }
107
-
108
- const hr = line.match(/^(-{3,}|\*{3,})\s*$/);
109
- if (hr) {
110
- flushList();
111
- out.push('<hr />');
112
- i++;
113
- continue;
114
- }
115
-
116
- const taskItem = line.match(/^\s*[-*]\s+\[( |x|X)\]\s+(.*)$/);
117
- if (taskItem) {
118
- if (listType !== 'ul') {
119
- flushList();
120
- listType = 'ul';
121
- }
122
- const checked = taskItem[1].toLowerCase() === 'x';
123
- listBuf.push(
124
- `<li class="task"><input type="checkbox" disabled ${checked ? 'checked' : ''} />${renderInline(taskItem[2])}</li>`
125
- );
126
- i++;
127
- continue;
128
- }
129
-
130
- const ulItem = line.match(/^\s*[-*]\s+(.*)$/);
131
- if (ulItem) {
132
- if (listType !== 'ul') {
133
- flushList();
134
- listType = 'ul';
135
- }
136
- listBuf.push(`<li>${renderInline(ulItem[1])}</li>`);
137
- i++;
138
- continue;
139
- }
140
-
141
- const olItem = line.match(/^\s*\d+\.\s+(.*)$/);
142
- if (olItem) {
143
- if (listType !== 'ol') {
144
- flushList();
145
- listType = 'ol';
146
- }
147
- listBuf.push(`<li>${renderInline(olItem[1])}</li>`);
148
- i++;
149
- continue;
150
- }
151
-
152
- if (line.trim() === '') {
153
- flushList();
154
- i++;
155
- continue;
156
- }
157
-
158
- flushList();
159
- out.push(`<p>${renderInline(line)}</p>`);
160
- i++;
161
- }
162
- flushList();
163
- if (inCodeBlock && codeBuf.length) {
164
- out.push(`<pre class="code-block"><code>${escapeHtml(codeBuf.join('\n'))}</code></pre>`);
165
- }
166
- return out.join('\n');
167
- }
168
- </script>
169
-
170
- <div class="readme-preview">
171
-
172
- <div class="preview-pane">
173
- <div class="markdown-body">
174
- {@html html}
175
- </div>
176
- </div>
177
- </div>
178
-
179
- <style>
180
- :root {
181
- --bg: var(--bg-1);
182
- --panel: var(--bg-blur);
183
- --panel-alt: var(--bg-blur);
184
- --border: var(--stroke);
185
- --text: var(--font-1);
186
- --muted: var(--font-3);
187
- --amber: var(--orange);
188
- --teal: var(--blur);
189
- --code-bg: var(--bg-2);
190
- }
191
-
192
- .readme-preview {
193
- display: flex;
194
- flex-direction: column;
195
- height: 640px;
196
- max-height: 90vh;
197
- background: var(--bg);
198
- color: var(--text);
199
- border: 1px solid var(--border);
200
- border-radius: 10px;
201
- overflow: hidden;
202
- font-family:
203
- 'Inter',
204
- -apple-system,
205
- BlinkMacSystemFont,
206
- 'Segoe UI',
207
- sans-serif;
208
- }
209
-
210
- .chrome {
211
- display: flex;
212
- align-items: center;
213
- background: var(--panel);
214
- border-bottom: 1px solid var(--border);
215
- padding: 0 10px;
216
- flex-shrink: 0;
217
- }
218
-
219
- .preview-pane {
220
- flex: 1;
221
- min-height: 0;
222
- overflow-y: auto;
223
- padding: 20px 28px;
224
- background: var(--panel);
225
- }
226
-
227
- .markdown-body {
228
- max-width: 640px;
229
- font-size: 14.5px;
230
- line-height: 1.7;
231
- color: var(--text);
232
- }
233
-
234
- .markdown-body :global(h1),
235
- .markdown-body :global(h2),
236
- .markdown-body :global(h3) {
237
- font-family: 'Inter', sans-serif;
238
- font-weight: 700;
239
- color: var(--text);
240
- margin: 1.1em 0 0.5em;
241
- line-height: 1.3;
242
- }
243
-
244
- .markdown-body :global(h1) {
245
- font-size: 1.7em;
246
- border-bottom: 1px solid var(--border);
247
- padding-bottom: 0.3em;
248
- }
249
-
250
- .markdown-body :global(h2) {
251
- font-size: 1.32em;
252
- border-bottom: 1px solid var(--border);
253
- padding-bottom: 0.25em;
254
- }
255
-
256
- .markdown-body :global(h3) {
257
- font-size: 1.1em;
258
- color: var(--teal);
259
- }
260
-
261
- .markdown-body :global(p) {
262
- margin: 0.6em 0;
263
- }
264
-
265
- .markdown-body :global(a) {
266
- color: var(--teal);
267
- text-decoration: none;
268
- border-bottom: 1px solid rgba(79, 209, 197, 0.35);
269
- }
270
-
271
- .markdown-body :global(a:hover) {
272
- border-bottom-color: var(--teal);
273
- }
274
-
275
- .markdown-body :global(code) {
276
- background: var(--code-bg);
277
- color: var(--amber);
278
- padding: 0.15em 0.4em;
279
- border-radius: 4px;
280
- font-family: 'JetBrains Mono', ui-monospace, monospace;
281
- font-size: 0.88em;
282
- }
283
-
284
- .markdown-body :global(.code-block) {
285
- background: var(--code-bg);
286
- border: 1px solid var(--border);
287
- border-radius: 8px;
288
- padding: 14px 16px;
289
- overflow-x: auto;
290
- margin: 0.8em 0;
291
- position: relative;
292
- }
293
-
294
- .markdown-body :global(.code-block::before) {
295
- content: attr(data-lang);
296
- position: absolute;
297
- top: 6px;
298
- right: 10px;
299
- font-size: 10px;
300
- letter-spacing: 0.05em;
301
- text-transform: uppercase;
302
- color: var(--muted);
303
- }
304
-
305
- .markdown-body :global(.code-block code) {
306
- background: none;
307
- color: var(--text);
308
- padding: 0;
309
- font-size: 0.85em;
310
- }
311
-
312
- .markdown-body :global(blockquote) {
313
- margin: 0.8em 0;
314
- padding: 0.4em 1em;
315
- border-left: 3px solid var(--amber);
316
- color: var(--muted);
317
- background: rgba(232, 179, 57, 0.05);
318
- }
319
-
320
- .markdown-body :global(ul),
321
- .markdown-body :global(ol) {
322
- margin: 0.6em 0;
323
- padding-left: 1.4em;
324
- }
325
-
326
- .markdown-body :global(li) {
327
- margin: 0.25em 0;
328
- }
329
-
330
- .markdown-body :global(li.task) {
331
- list-style: none;
332
- margin-left: -1.4em;
333
- display: flex;
334
- align-items: baseline;
335
- gap: 6px;
336
- }
337
-
338
- .markdown-body :global(hr) {
339
- border: none;
340
- border-top: 1px solid var(--border);
341
- margin: 1.4em 0;
342
- }
343
-
344
- .markdown-body :global(img) {
345
- max-width: 100%;
346
- border-radius: 6px;
347
- }
348
-
349
- .markdown-body :global(strong) {
350
- color: var(--text);
351
- }
352
-
353
- .readme-preview :global(input[type='checkbox']) {
354
- appearance: none;
355
- -webkit-appearance: none;
356
- width: 16px;
357
- height: 16px;
358
- flex-shrink: 0;
359
- margin: 0;
360
- border: 1.5px solid var(--border, #262c3a);
361
- border-radius: 4px;
362
- background: var(--code-bg, #0d0f16);
363
- cursor: default;
364
- position: relative;
365
- }
366
-
367
- .readme-preview :global(input[type='checkbox']:checked) {
368
- background: var(--amber, #e8b339);
369
- border-color: var(--amber, #e8b339);
370
- }
371
-
372
- .readme-preview :global(input[type='checkbox']:checked::after) {
373
- content: '';
374
- position: absolute;
375
- left: 4px;
376
- top: 1px;
377
- width: 4px;
378
- height: 8px;
379
- border: solid var(--font-1);
380
- border-width: 0 2px 2px 0;
381
- transform: rotate(45deg);
382
- }
383
- </style>