@semantic-components/code 0.63.0 → 0.64.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.
Files changed (64) hide show
  1. package/esm2022/index.js +2 -0
  2. package/esm2022/index.js.map +1 -0
  3. package/esm2022/lib/components/code-editor/code-editor-content.js +435 -0
  4. package/esm2022/lib/components/code-editor/code-editor-content.js.map +1 -0
  5. package/esm2022/lib/components/code-editor/code-editor-copy-button.js +90 -0
  6. package/esm2022/lib/components/code-editor/code-editor-copy-button.js.map +1 -0
  7. package/esm2022/lib/components/code-editor/code-editor-footer.js +27 -0
  8. package/esm2022/lib/components/code-editor/code-editor-footer.js.map +1 -0
  9. package/esm2022/lib/components/code-editor/code-editor-header.js +27 -0
  10. package/esm2022/lib/components/code-editor/code-editor-header.js.map +1 -0
  11. package/esm2022/lib/components/code-editor/code-editor-label.js +27 -0
  12. package/esm2022/lib/components/code-editor/code-editor-label.js.map +1 -0
  13. package/esm2022/lib/components/code-editor/code-editor.js +27 -0
  14. package/esm2022/lib/components/code-editor/code-editor.js.map +1 -0
  15. package/esm2022/lib/components/code-editor/index.js +7 -0
  16. package/esm2022/lib/components/code-editor/index.js.map +1 -0
  17. package/esm2022/lib/components/code-viewer/code-viewer-content.js +70 -0
  18. package/esm2022/lib/components/code-viewer/code-viewer-content.js.map +1 -0
  19. package/esm2022/lib/components/code-viewer/code-viewer-header.js +27 -0
  20. package/esm2022/lib/components/code-viewer/code-viewer-header.js.map +1 -0
  21. package/esm2022/lib/components/code-viewer/code-viewer-label.js +27 -0
  22. package/esm2022/lib/components/code-viewer/code-viewer-label.js.map +1 -0
  23. package/esm2022/lib/components/code-viewer/code-viewer.js +27 -0
  24. package/esm2022/lib/components/code-viewer/code-viewer.js.map +1 -0
  25. package/esm2022/lib/components/code-viewer/index.js +5 -0
  26. package/esm2022/lib/components/code-viewer/index.js.map +1 -0
  27. package/esm2022/lib/components/index.js +3 -0
  28. package/esm2022/lib/components/index.js.map +1 -0
  29. package/esm2022/semantic-components-code.js +5 -0
  30. package/esm2022/semantic-components-code.js.map +1 -0
  31. package/lib/components/code-editor/code-editor-content.d.ts +57 -0
  32. package/lib/components/code-editor/code-editor-copy-button.d.ts +11 -0
  33. package/lib/components/code-editor/code-editor-footer.d.ts +7 -0
  34. package/lib/components/code-editor/code-editor-header.d.ts +7 -0
  35. package/lib/components/code-editor/code-editor-label.d.ts +7 -0
  36. package/lib/components/code-editor/code-editor.d.ts +7 -0
  37. package/lib/components/code-viewer/code-viewer-content.d.ts +17 -0
  38. package/lib/components/code-viewer/code-viewer-header.d.ts +7 -0
  39. package/lib/components/code-viewer/code-viewer-label.d.ts +7 -0
  40. package/lib/components/code-viewer/code-viewer.d.ts +7 -0
  41. package/package.json +15 -3
  42. package/semantic-components-code.d.ts +5 -0
  43. package/eslint.config.mjs +0 -48
  44. package/ng-package.json +0 -8
  45. package/project.json +0 -28
  46. package/src/lib/components/code-editor/README.md +0 -368
  47. package/src/lib/components/code-editor/code-editor-content.ts +0 -507
  48. package/src/lib/components/code-editor/code-editor-copy-button.ts +0 -77
  49. package/src/lib/components/code-editor/code-editor-footer.ts +0 -31
  50. package/src/lib/components/code-editor/code-editor-header.ts +0 -31
  51. package/src/lib/components/code-editor/code-editor-label.ts +0 -28
  52. package/src/lib/components/code-editor/code-editor.ts +0 -31
  53. package/src/lib/components/code-viewer/README.md +0 -178
  54. package/src/lib/components/code-viewer/code-viewer-content.ts +0 -96
  55. package/src/lib/components/code-viewer/code-viewer-header.ts +0 -31
  56. package/src/lib/components/code-viewer/code-viewer-label.ts +0 -28
  57. package/src/lib/components/code-viewer/code-viewer.ts +0 -28
  58. package/tsconfig.json +0 -28
  59. package/tsconfig.lib.json +0 -12
  60. package/tsconfig.lib.prod.json +0 -7
  61. /package/{src/index.ts → index.d.ts} +0 -0
  62. /package/{src/lib/components/code-editor/index.ts → lib/components/code-editor/index.d.ts} +0 -0
  63. /package/{src/lib/components/code-viewer/index.ts → lib/components/code-viewer/index.d.ts} +0 -0
  64. /package/{src/lib/components/index.ts → lib/components/index.d.ts} +0 -0
@@ -0,0 +1,2 @@
1
+ export * from './lib/components';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/code/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC","sourcesContent":["export * from './lib/components';\n"]}
@@ -0,0 +1,435 @@
1
+ import { ChangeDetectionStrategy, Component, computed, effect, inject, input, model, output, signal, viewChild, ViewEncapsulation, } from '@angular/core';
2
+ import { DomSanitizer } from '@angular/platform-browser';
3
+ import { codeToHtml } from 'shiki';
4
+ import { cn } from '@semantic-components/ui';
5
+ import * as i0 from "@angular/core";
6
+ const EXTENSION_MAP = {
7
+ js: 'javascript',
8
+ mjs: 'javascript',
9
+ cjs: 'javascript',
10
+ jsx: 'javascript',
11
+ ts: 'typescript',
12
+ mts: 'typescript',
13
+ cts: 'typescript',
14
+ tsx: 'typescript',
15
+ html: 'html',
16
+ htm: 'html',
17
+ css: 'css',
18
+ scss: 'css',
19
+ sass: 'css',
20
+ less: 'css',
21
+ json: 'json',
22
+ py: 'python',
23
+ pyw: 'python',
24
+ sh: 'bash',
25
+ bash: 'bash',
26
+ zsh: 'shell',
27
+ sql: 'sql',
28
+ md: 'markdown',
29
+ markdown: 'markdown',
30
+ yaml: 'yaml',
31
+ yml: 'yaml',
32
+ go: 'go',
33
+ rs: 'rust',
34
+ java: 'java',
35
+ txt: 'plaintext',
36
+ };
37
+ export function detectLanguage(code, filename) {
38
+ if (filename) {
39
+ const ext = filename.split('.').pop()?.toLowerCase();
40
+ if (ext && EXTENSION_MAP[ext]) {
41
+ return EXTENSION_MAP[ext];
42
+ }
43
+ }
44
+ // Try to detect from content
45
+ if (code.includes('<!DOCTYPE') ||
46
+ code.includes('<html') ||
47
+ /<\w+[^>]*>/.test(code)) {
48
+ return 'html';
49
+ }
50
+ if (/^\s*\{[\s\S]*\}\s*$/.test(code) || /^\s*\[[\s\S]*\]\s*$/.test(code)) {
51
+ try {
52
+ JSON.parse(code);
53
+ return 'json';
54
+ }
55
+ catch {
56
+ // Not JSON
57
+ }
58
+ }
59
+ if (/^(import|export|const|let|var|function|class)\s/.test(code)) {
60
+ if (/:\s*(string|number|boolean|any|void|never)\b/.test(code) ||
61
+ /interface\s+\w+/.test(code)) {
62
+ return 'typescript';
63
+ }
64
+ return 'javascript';
65
+ }
66
+ if (/^(def|class|import|from|if __name__)\s/.test(code) ||
67
+ /:\s*$/.test(code.split('\n')[0])) {
68
+ return 'python';
69
+ }
70
+ if (/^(SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP)\s/i.test(code)) {
71
+ return 'sql';
72
+ }
73
+ if (/^#\s/.test(code) || /^\*\*.*\*\*$/.test(code)) {
74
+ return 'markdown';
75
+ }
76
+ if (/^#!/.test(code)) {
77
+ return 'bash';
78
+ }
79
+ return 'plaintext';
80
+ }
81
+ export class ScCodeEditorContent {
82
+ // Two-way binding for value
83
+ value = model('', ...(ngDevMode ? [{ debugName: "value" }] : []));
84
+ // Inputs
85
+ language = input('plaintext', ...(ngDevMode ? [{ debugName: "language" }] : []));
86
+ filename = input('', ...(ngDevMode ? [{ debugName: "filename" }] : []));
87
+ placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
88
+ disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
89
+ readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : []));
90
+ showLineNumbers = input(true, ...(ngDevMode ? [{ debugName: "showLineNumbers" }] : []));
91
+ tabSize = input(2, ...(ngDevMode ? [{ debugName: "tabSize" }] : []));
92
+ insertSpaces = input(true, ...(ngDevMode ? [{ debugName: "insertSpaces" }] : []));
93
+ wordWrap = input(false, ...(ngDevMode ? [{ debugName: "wordWrap" }] : []));
94
+ autoDetectLanguage = input(false, ...(ngDevMode ? [{ debugName: "autoDetectLanguage" }] : []));
95
+ ariaLabel = input('', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
96
+ ariaDescribedby = input('', ...(ngDevMode ? [{ debugName: "ariaDescribedby" }] : []));
97
+ classInput = input('', { ...(ngDevMode ? { debugName: "classInput" } : {}), alias: 'class' });
98
+ // Outputs
99
+ valueChange = output();
100
+ languageDetected = output();
101
+ cursorChange = output();
102
+ // Internal state
103
+ activeLine = signal(1, ...(ngDevMode ? [{ debugName: "activeLine" }] : []));
104
+ activeColumn = signal(1, ...(ngDevMode ? [{ debugName: "activeColumn" }] : []));
105
+ isFocused = signal(false, ...(ngDevMode ? [{ debugName: "isFocused" }] : []));
106
+ highlightedHtml = signal(null, ...(ngDevMode ? [{ debugName: "highlightedHtml" }] : []));
107
+ scrollTop = 0;
108
+ scrollLeft = 0;
109
+ sanitizer = inject(DomSanitizer);
110
+ textarea = viewChild.required('textarea');
111
+ lines = computed(() => {
112
+ const code = this.value() || '';
113
+ return code.split('\n');
114
+ }, ...(ngDevMode ? [{ debugName: "lines" }] : []));
115
+ lineNumberWidth = computed(() => {
116
+ return Math.max(2, String(this.lines().length).length + 1);
117
+ }, ...(ngDevMode ? [{ debugName: "lineNumberWidth" }] : []));
118
+ displayCode = computed(() => {
119
+ const code = this.value() || '';
120
+ return code.endsWith('\n') ? code : code + '\n';
121
+ }, ...(ngDevMode ? [{ debugName: "displayCode" }] : []));
122
+ effectiveLanguage = computed(() => {
123
+ if (this.autoDetectLanguage() && this.value()) {
124
+ return detectLanguage(this.value(), this.filename());
125
+ }
126
+ return this.language();
127
+ }, ...(ngDevMode ? [{ debugName: "effectiveLanguage" }] : []));
128
+ wrapperClass = computed(() => cn('relative flex overflow-hidden', this.classInput()), ...(ngDevMode ? [{ debugName: "wrapperClass" }] : []));
129
+ textareaClass = computed(() => cn('relative m-0 w-full resize-none p-3', 'border-none bg-transparent text-transparent caret-current outline-none', 'font-mono text-sm leading-relaxed', this.disabled() && 'cursor-not-allowed'), ...(ngDevMode ? [{ debugName: "textareaClass" }] : []));
130
+ textareaStyle = computed(() => ({
131
+ whiteSpace: this.wordWrap() ? 'pre-wrap' : 'pre',
132
+ wordBreak: this.wordWrap() ? 'break-all' : 'normal',
133
+ overflowWrap: this.wordWrap() ? 'break-word' : 'normal',
134
+ }), ...(ngDevMode ? [{ debugName: "textareaStyle" }] : []));
135
+ constructor() {
136
+ // Effect to trigger Shiki highlighting
137
+ effect(() => {
138
+ const code = this.displayCode();
139
+ const lang = this.effectiveLanguage();
140
+ this.highlight(code, lang);
141
+ });
142
+ // Effect for language detection notification
143
+ effect(() => {
144
+ if (this.autoDetectLanguage() && this.value()) {
145
+ const detected = detectLanguage(this.value(), this.filename());
146
+ this.languageDetected.emit(detected);
147
+ }
148
+ });
149
+ }
150
+ async highlight(code, lang) {
151
+ try {
152
+ const html = await codeToHtml(code, {
153
+ lang,
154
+ themes: {
155
+ light: 'github-light',
156
+ dark: 'github-dark',
157
+ },
158
+ defaultColor: false,
159
+ });
160
+ this.highlightedHtml.set(this.sanitizer.bypassSecurityTrustHtml(html));
161
+ }
162
+ catch {
163
+ this.highlightedHtml.set(null);
164
+ }
165
+ }
166
+ focusTextarea() {
167
+ if (!this.disabled() && !this.readonly()) {
168
+ this.textarea().nativeElement.focus();
169
+ }
170
+ }
171
+ onInput(event) {
172
+ const target = event.target;
173
+ this.value.set(target.value);
174
+ this.valueChange.emit(target.value);
175
+ this.updateCursorPosition(target);
176
+ }
177
+ onKeydown(event) {
178
+ const target = event.target;
179
+ // Handle Tab key
180
+ if (event.key === 'Tab') {
181
+ event.preventDefault();
182
+ const start = target.selectionStart;
183
+ const end = target.selectionEnd;
184
+ const value = target.value;
185
+ const indent = this.insertSpaces() ? ' '.repeat(this.tabSize()) : '\t';
186
+ if (event.shiftKey) {
187
+ // Outdent
188
+ const lineStart = value.lastIndexOf('\n', start - 1) + 1;
189
+ const lineContent = value.slice(lineStart, start);
190
+ const indentMatch = lineContent.match(/^(\t| {1,})/);
191
+ if (indentMatch) {
192
+ const removeLength = Math.min(indentMatch[1].length, this.insertSpaces() ? this.tabSize() : 1);
193
+ const newValue = value.slice(0, lineStart) + value.slice(lineStart + removeLength);
194
+ this.value.set(newValue);
195
+ this.valueChange.emit(newValue);
196
+ // Restore cursor position
197
+ setTimeout(() => {
198
+ target.selectionStart = target.selectionEnd = start - removeLength;
199
+ });
200
+ }
201
+ }
202
+ else {
203
+ // Indent
204
+ const newValue = value.slice(0, start) + indent + value.slice(end);
205
+ this.value.set(newValue);
206
+ this.valueChange.emit(newValue);
207
+ // Move cursor after indent
208
+ setTimeout(() => {
209
+ target.selectionStart = target.selectionEnd = start + indent.length;
210
+ });
211
+ }
212
+ }
213
+ // Handle Enter for auto-indent
214
+ if (event.key === 'Enter' && !event.shiftKey) {
215
+ const start = target.selectionStart;
216
+ const value = target.value;
217
+ // Find the current line's indentation
218
+ const lineStart = value.lastIndexOf('\n', start - 1) + 1;
219
+ const lineContent = value.slice(lineStart, start);
220
+ const indentMatch = lineContent.match(/^(\s*)/);
221
+ const currentIndent = indentMatch ? indentMatch[1] : '';
222
+ // Check if we need extra indent (after { or :)
223
+ const charBefore = value[start - 1];
224
+ const extraIndent = charBefore === '{' || charBefore === ':' || charBefore === '['
225
+ ? this.insertSpaces()
226
+ ? ' '.repeat(this.tabSize())
227
+ : '\t'
228
+ : '';
229
+ event.preventDefault();
230
+ const newValue = value.slice(0, start) +
231
+ '\n' +
232
+ currentIndent +
233
+ extraIndent +
234
+ value.slice(start);
235
+ this.value.set(newValue);
236
+ this.valueChange.emit(newValue);
237
+ setTimeout(() => {
238
+ const newPos = start + 1 + currentIndent.length + extraIndent.length;
239
+ target.selectionStart = target.selectionEnd = newPos;
240
+ this.updateCursorPosition(target);
241
+ });
242
+ }
243
+ // Handle closing brackets
244
+ if (event.key === '}' || event.key === ']' || event.key === ')') {
245
+ const start = target.selectionStart;
246
+ const value = target.value;
247
+ const lineStart = value.lastIndexOf('\n', start - 1) + 1;
248
+ const beforeCursor = value.slice(lineStart, start);
249
+ // If line is only whitespace, reduce indent
250
+ if (/^\s+$/.test(beforeCursor)) {
251
+ const reduceBy = this.insertSpaces() ? this.tabSize() : 1;
252
+ const newIndent = beforeCursor.slice(0, Math.max(0, beforeCursor.length - reduceBy));
253
+ event.preventDefault();
254
+ const newValue = value.slice(0, lineStart) +
255
+ newIndent +
256
+ event.key +
257
+ value.slice(start);
258
+ this.value.set(newValue);
259
+ this.valueChange.emit(newValue);
260
+ setTimeout(() => {
261
+ target.selectionStart = target.selectionEnd =
262
+ lineStart + newIndent.length + 1;
263
+ });
264
+ }
265
+ }
266
+ }
267
+ onScroll(event) {
268
+ const target = event.target;
269
+ this.scrollTop = target.scrollTop;
270
+ this.scrollLeft = target.scrollLeft;
271
+ // Sync scroll with the display layer
272
+ const displayLayer = target.previousElementSibling;
273
+ if (displayLayer) {
274
+ displayLayer.scrollTop = this.scrollTop;
275
+ displayLayer.scrollLeft = this.scrollLeft;
276
+ }
277
+ }
278
+ onFocus() {
279
+ this.isFocused.set(true);
280
+ }
281
+ onBlur() {
282
+ this.isFocused.set(false);
283
+ }
284
+ updateActiveLine(event) {
285
+ const target = event.target;
286
+ this.updateCursorPosition(target);
287
+ }
288
+ updateCursorPosition(textarea) {
289
+ const value = textarea.value;
290
+ const pos = textarea.selectionStart;
291
+ // Calculate line and column
292
+ const textBeforeCursor = value.slice(0, pos);
293
+ const lines = textBeforeCursor.split('\n');
294
+ const line = lines.length;
295
+ const column = lines[lines.length - 1].length + 1;
296
+ this.activeLine.set(line);
297
+ this.activeColumn.set(column);
298
+ this.cursorChange.emit({ line, column });
299
+ }
300
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ScCodeEditorContent, deps: [], target: i0.ɵɵFactoryTarget.Component });
301
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.4", type: ScCodeEditorContent, isStandalone: true, selector: "div[sc-code-editor-content]", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, language: { classPropertyName: "language", publicName: "language", isSignal: true, isRequired: false, transformFunction: null }, filename: { classPropertyName: "filename", publicName: "filename", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, showLineNumbers: { classPropertyName: "showLineNumbers", publicName: "showLineNumbers", isSignal: true, isRequired: false, transformFunction: null }, tabSize: { classPropertyName: "tabSize", publicName: "tabSize", isSignal: true, isRequired: false, transformFunction: null }, insertSpaces: { classPropertyName: "insertSpaces", publicName: "insertSpaces", isSignal: true, isRequired: false, transformFunction: null }, wordWrap: { classPropertyName: "wordWrap", publicName: "wordWrap", isSignal: true, isRequired: false, transformFunction: null }, autoDetectLanguage: { classPropertyName: "autoDetectLanguage", publicName: "autoDetectLanguage", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "ariaDescribedby", isSignal: true, isRequired: false, transformFunction: null }, classInput: { classPropertyName: "classInput", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", valueChange: "valueChange", languageDetected: "languageDetected", cursorChange: "cursorChange" }, host: { attributes: { "data-slot": "code-editor-content" }, listeners: { "click": "focusTextarea()" }, properties: { "class": "wrapperClass()" } }, viewQueries: [{ propertyName: "textarea", first: true, predicate: ["textarea"], descendants: true, isSignal: true }], ngImport: i0, template: `
302
+ <!-- Line numbers -->
303
+ @if (showLineNumbers()) {
304
+ <div
305
+ class="sc-code-editor-content__line-numbers flex-shrink-0 select-none border-r border-border py-3 pl-3 pr-3 text-right"
306
+ [style.min-width.ch]="lineNumberWidth()"
307
+ aria-hidden="true"
308
+ >
309
+ @for (line of lines(); track $index) {
310
+ <div
311
+ class="font-mono text-sm leading-relaxed"
312
+ [class.sc-code-editor-content__line-numbers--active]="
313
+ activeLine() === $index + 1
314
+ "
315
+ >
316
+ {{ $index + 1 }}
317
+ </div>
318
+ }
319
+ </div>
320
+ }
321
+
322
+ <!-- Code area -->
323
+ <div class="relative min-w-0 flex-1">
324
+ <!-- Highlighted code (display layer) -->
325
+ <div
326
+ class="pointer-events-none absolute inset-0 overflow-hidden"
327
+ [class.word-wrap-enabled]="wordWrap()"
328
+ aria-hidden="true"
329
+ >
330
+ @if (highlightedHtml()) {
331
+ <div [innerHTML]="highlightedHtml()"></div>
332
+ } @else {
333
+ <pre
334
+ class="m-0 p-3 font-mono text-sm leading-relaxed"
335
+ ><code>{{ displayCode() }}</code></pre>
336
+ }
337
+ </div>
338
+
339
+ <!-- Textarea (input layer) -->
340
+ <textarea
341
+ #textarea
342
+ [value]="value()"
343
+ (input)="onInput($event)"
344
+ (keydown)="onKeydown($event)"
345
+ (scroll)="onScroll($event)"
346
+ (focus)="onFocus()"
347
+ (blur)="onBlur()"
348
+ (click)="updateActiveLine($event)"
349
+ (keyup)="updateActiveLine($event)"
350
+ [disabled]="disabled()"
351
+ [readonly]="readonly()"
352
+ [placeholder]="placeholder()"
353
+ [attr.aria-label]="ariaLabel() || 'Code editor'"
354
+ [attr.aria-describedby]="ariaDescribedby()"
355
+ autocomplete="off"
356
+ autocorrect="off"
357
+ autocapitalize="off"
358
+ spellcheck="false"
359
+ [class]="textareaClass()"
360
+ [style]="textareaStyle()"
361
+ ></textarea>
362
+ </div>
363
+ `, isInline: true, styles: [".sc-code-editor-content__line-numbers{color:oklch(from var(--muted-foreground) l c h / .5)}.sc-code-editor-content__line-numbers--active{color:var(--foreground)}[sc-code-editor-content] pre.shiki,[sc-code-editor-content] pre.shiki span{background-color:transparent!important}[sc-code-editor-content] .word-wrap-enabled pre.shiki{white-space:pre-wrap;word-break:break-all}[sc-code-editor-content] textarea{caret-color:var(--primary)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
364
+ }
365
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ScCodeEditorContent, decorators: [{
366
+ type: Component,
367
+ args: [{ selector: 'div[sc-code-editor-content]', template: `
368
+ <!-- Line numbers -->
369
+ @if (showLineNumbers()) {
370
+ <div
371
+ class="sc-code-editor-content__line-numbers flex-shrink-0 select-none border-r border-border py-3 pl-3 pr-3 text-right"
372
+ [style.min-width.ch]="lineNumberWidth()"
373
+ aria-hidden="true"
374
+ >
375
+ @for (line of lines(); track $index) {
376
+ <div
377
+ class="font-mono text-sm leading-relaxed"
378
+ [class.sc-code-editor-content__line-numbers--active]="
379
+ activeLine() === $index + 1
380
+ "
381
+ >
382
+ {{ $index + 1 }}
383
+ </div>
384
+ }
385
+ </div>
386
+ }
387
+
388
+ <!-- Code area -->
389
+ <div class="relative min-w-0 flex-1">
390
+ <!-- Highlighted code (display layer) -->
391
+ <div
392
+ class="pointer-events-none absolute inset-0 overflow-hidden"
393
+ [class.word-wrap-enabled]="wordWrap()"
394
+ aria-hidden="true"
395
+ >
396
+ @if (highlightedHtml()) {
397
+ <div [innerHTML]="highlightedHtml()"></div>
398
+ } @else {
399
+ <pre
400
+ class="m-0 p-3 font-mono text-sm leading-relaxed"
401
+ ><code>{{ displayCode() }}</code></pre>
402
+ }
403
+ </div>
404
+
405
+ <!-- Textarea (input layer) -->
406
+ <textarea
407
+ #textarea
408
+ [value]="value()"
409
+ (input)="onInput($event)"
410
+ (keydown)="onKeydown($event)"
411
+ (scroll)="onScroll($event)"
412
+ (focus)="onFocus()"
413
+ (blur)="onBlur()"
414
+ (click)="updateActiveLine($event)"
415
+ (keyup)="updateActiveLine($event)"
416
+ [disabled]="disabled()"
417
+ [readonly]="readonly()"
418
+ [placeholder]="placeholder()"
419
+ [attr.aria-label]="ariaLabel() || 'Code editor'"
420
+ [attr.aria-describedby]="ariaDescribedby()"
421
+ autocomplete="off"
422
+ autocorrect="off"
423
+ autocapitalize="off"
424
+ spellcheck="false"
425
+ [class]="textareaClass()"
426
+ [style]="textareaStyle()"
427
+ ></textarea>
428
+ </div>
429
+ `, host: {
430
+ 'data-slot': 'code-editor-content',
431
+ '[class]': 'wrapperClass()',
432
+ '(click)': 'focusTextarea()',
433
+ }, encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".sc-code-editor-content__line-numbers{color:oklch(from var(--muted-foreground) l c h / .5)}.sc-code-editor-content__line-numbers--active{color:var(--foreground)}[sc-code-editor-content] pre.shiki,[sc-code-editor-content] pre.shiki span{background-color:transparent!important}[sc-code-editor-content] .word-wrap-enabled pre.shiki{white-space:pre-wrap;word-break:break-all}[sc-code-editor-content] textarea{caret-color:var(--primary)}\n"] }]
434
+ }], ctorParameters: () => [], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], language: [{ type: i0.Input, args: [{ isSignal: true, alias: "language", required: false }] }], filename: [{ type: i0.Input, args: [{ isSignal: true, alias: "filename", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], showLineNumbers: [{ type: i0.Input, args: [{ isSignal: true, alias: "showLineNumbers", required: false }] }], tabSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabSize", required: false }] }], insertSpaces: [{ type: i0.Input, args: [{ isSignal: true, alias: "insertSpaces", required: false }] }], wordWrap: [{ type: i0.Input, args: [{ isSignal: true, alias: "wordWrap", required: false }] }], autoDetectLanguage: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoDetectLanguage", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedby", required: false }] }], classInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], languageDetected: [{ type: i0.Output, args: ["languageDetected"] }], cursorChange: [{ type: i0.Output, args: ["cursorChange"] }], textarea: [{ type: i0.ViewChild, args: ['textarea', { isSignal: true }] }] } });
435
+ //# sourceMappingURL=code-editor-content.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor-content.js","sourceRoot":"","sources":["../../../../../../../libs/code/src/lib/components/code-editor/code-editor-content.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n effect,\n ElementRef,\n inject,\n input,\n model,\n output,\n signal,\n viewChild,\n ViewEncapsulation,\n} from '@angular/core';\nimport { DomSanitizer, SafeHtml } from '@angular/platform-browser';\nimport { codeToHtml } from 'shiki';\nimport { cn } from '@semantic-components/ui';\n\nexport type ScCodeEditorLanguage =\n | 'angular-ts'\n | 'typescript'\n | 'javascript'\n | 'html'\n | 'css'\n | 'json'\n | 'python'\n | 'bash'\n | 'shell'\n | 'markdown'\n | 'yaml'\n | 'sql'\n | 'go'\n | 'rust'\n | 'java'\n | 'plaintext';\n\nconst EXTENSION_MAP: Record<string, ScCodeEditorLanguage> = {\n js: 'javascript',\n mjs: 'javascript',\n cjs: 'javascript',\n jsx: 'javascript',\n ts: 'typescript',\n mts: 'typescript',\n cts: 'typescript',\n tsx: 'typescript',\n html: 'html',\n htm: 'html',\n css: 'css',\n scss: 'css',\n sass: 'css',\n less: 'css',\n json: 'json',\n py: 'python',\n pyw: 'python',\n sh: 'bash',\n bash: 'bash',\n zsh: 'shell',\n sql: 'sql',\n md: 'markdown',\n markdown: 'markdown',\n yaml: 'yaml',\n yml: 'yaml',\n go: 'go',\n rs: 'rust',\n java: 'java',\n txt: 'plaintext',\n};\n\nexport function detectLanguage(\n code: string,\n filename?: string,\n): ScCodeEditorLanguage {\n if (filename) {\n const ext = filename.split('.').pop()?.toLowerCase();\n if (ext && EXTENSION_MAP[ext]) {\n return EXTENSION_MAP[ext];\n }\n }\n\n // Try to detect from content\n if (\n code.includes('<!DOCTYPE') ||\n code.includes('<html') ||\n /<\\w+[^>]*>/.test(code)\n ) {\n return 'html';\n }\n if (/^\\s*\\{[\\s\\S]*\\}\\s*$/.test(code) || /^\\s*\\[[\\s\\S]*\\]\\s*$/.test(code)) {\n try {\n JSON.parse(code);\n return 'json';\n } catch {\n // Not JSON\n }\n }\n if (/^(import|export|const|let|var|function|class)\\s/.test(code)) {\n if (\n /:\\s*(string|number|boolean|any|void|never)\\b/.test(code) ||\n /interface\\s+\\w+/.test(code)\n ) {\n return 'typescript';\n }\n return 'javascript';\n }\n if (\n /^(def|class|import|from|if __name__)\\s/.test(code) ||\n /:\\s*$/.test(code.split('\\n')[0])\n ) {\n return 'python';\n }\n if (/^(SELECT|INSERT|UPDATE|DELETE|CREATE|ALTER|DROP)\\s/i.test(code)) {\n return 'sql';\n }\n if (/^#\\s/.test(code) || /^\\*\\*.*\\*\\*$/.test(code)) {\n return 'markdown';\n }\n if (/^#!/.test(code)) {\n return 'bash';\n }\n\n return 'plaintext';\n}\n\n@Component({\n selector: 'div[sc-code-editor-content]',\n template: `\n <!-- Line numbers -->\n @if (showLineNumbers()) {\n <div\n class=\"sc-code-editor-content__line-numbers flex-shrink-0 select-none border-r border-border py-3 pl-3 pr-3 text-right\"\n [style.min-width.ch]=\"lineNumberWidth()\"\n aria-hidden=\"true\"\n >\n @for (line of lines(); track $index) {\n <div\n class=\"font-mono text-sm leading-relaxed\"\n [class.sc-code-editor-content__line-numbers--active]=\"\n activeLine() === $index + 1\n \"\n >\n {{ $index + 1 }}\n </div>\n }\n </div>\n }\n\n <!-- Code area -->\n <div class=\"relative min-w-0 flex-1\">\n <!-- Highlighted code (display layer) -->\n <div\n class=\"pointer-events-none absolute inset-0 overflow-hidden\"\n [class.word-wrap-enabled]=\"wordWrap()\"\n aria-hidden=\"true\"\n >\n @if (highlightedHtml()) {\n <div [innerHTML]=\"highlightedHtml()\"></div>\n } @else {\n <pre\n class=\"m-0 p-3 font-mono text-sm leading-relaxed\"\n ><code>{{ displayCode() }}</code></pre>\n }\n </div>\n\n <!-- Textarea (input layer) -->\n <textarea\n #textarea\n [value]=\"value()\"\n (input)=\"onInput($event)\"\n (keydown)=\"onKeydown($event)\"\n (scroll)=\"onScroll($event)\"\n (focus)=\"onFocus()\"\n (blur)=\"onBlur()\"\n (click)=\"updateActiveLine($event)\"\n (keyup)=\"updateActiveLine($event)\"\n [disabled]=\"disabled()\"\n [readonly]=\"readonly()\"\n [placeholder]=\"placeholder()\"\n [attr.aria-label]=\"ariaLabel() || 'Code editor'\"\n [attr.aria-describedby]=\"ariaDescribedby()\"\n autocomplete=\"off\"\n autocorrect=\"off\"\n autocapitalize=\"off\"\n spellcheck=\"false\"\n [class]=\"textareaClass()\"\n [style]=\"textareaStyle()\"\n ></textarea>\n </div>\n `,\n styles: `\n .sc-code-editor-content__line-numbers {\n color: oklch(from var(--muted-foreground) l c h / 0.5);\n }\n\n .sc-code-editor-content__line-numbers--active {\n color: var(--foreground);\n }\n\n /* Transparent background for editor overlay effect */\n [sc-code-editor-content] pre.shiki,\n [sc-code-editor-content] pre.shiki span {\n background-color: transparent !important;\n }\n\n /* Word wrap support */\n [sc-code-editor-content] .word-wrap-enabled pre.shiki {\n white-space: pre-wrap;\n word-break: break-all;\n }\n\n /* Caret color */\n [sc-code-editor-content] textarea {\n caret-color: var(--primary);\n }\n `,\n host: {\n 'data-slot': 'code-editor-content',\n '[class]': 'wrapperClass()',\n '(click)': 'focusTextarea()',\n },\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ScCodeEditorContent {\n // Two-way binding for value\n readonly value = model<string>('');\n\n // Inputs\n readonly language = input<ScCodeEditorLanguage>('plaintext');\n readonly filename = input<string>('');\n readonly placeholder = input<string>('');\n readonly disabled = input(false);\n readonly readonly = input(false);\n readonly showLineNumbers = input(true);\n readonly tabSize = input(2);\n readonly insertSpaces = input(true);\n readonly wordWrap = input(false);\n readonly autoDetectLanguage = input(false);\n readonly ariaLabel = input<string>('');\n readonly ariaDescribedby = input<string>('');\n readonly classInput = input<string>('', { alias: 'class' });\n\n // Outputs\n readonly valueChange = output<string>();\n readonly languageDetected = output<ScCodeEditorLanguage>();\n readonly cursorChange = output<{ line: number; column: number }>();\n\n // Internal state\n readonly activeLine = signal(1);\n readonly activeColumn = signal(1);\n readonly isFocused = signal(false);\n protected readonly highlightedHtml = signal<SafeHtml | null>(null);\n private scrollTop = 0;\n private scrollLeft = 0;\n\n private readonly sanitizer = inject(DomSanitizer);\n private readonly textarea =\n viewChild.required<ElementRef<HTMLTextAreaElement>>('textarea');\n\n protected readonly lines = computed(() => {\n const code = this.value() || '';\n return code.split('\\n');\n });\n\n protected readonly lineNumberWidth = computed(() => {\n return Math.max(2, String(this.lines().length).length + 1);\n });\n\n protected readonly displayCode = computed(() => {\n const code = this.value() || '';\n return code.endsWith('\\n') ? code : code + '\\n';\n });\n\n protected readonly effectiveLanguage = computed(() => {\n if (this.autoDetectLanguage() && this.value()) {\n return detectLanguage(this.value(), this.filename());\n }\n return this.language();\n });\n\n protected readonly wrapperClass = computed(() =>\n cn('relative flex overflow-hidden', this.classInput()),\n );\n\n protected readonly textareaClass = computed(() =>\n cn(\n 'relative m-0 w-full resize-none p-3',\n 'border-none bg-transparent text-transparent caret-current outline-none',\n 'font-mono text-sm leading-relaxed',\n this.disabled() && 'cursor-not-allowed',\n ),\n );\n\n protected readonly textareaStyle = computed(() => ({\n whiteSpace: this.wordWrap() ? 'pre-wrap' : 'pre',\n wordBreak: this.wordWrap() ? 'break-all' : 'normal',\n overflowWrap: this.wordWrap() ? 'break-word' : 'normal',\n }));\n\n constructor() {\n // Effect to trigger Shiki highlighting\n effect(() => {\n const code = this.displayCode();\n const lang = this.effectiveLanguage();\n\n this.highlight(code, lang);\n });\n\n // Effect for language detection notification\n effect(() => {\n if (this.autoDetectLanguage() && this.value()) {\n const detected = detectLanguage(this.value(), this.filename());\n this.languageDetected.emit(detected);\n }\n });\n }\n\n private async highlight(\n code: string,\n lang: ScCodeEditorLanguage,\n ): Promise<void> {\n try {\n const html = await codeToHtml(code, {\n lang,\n themes: {\n light: 'github-light',\n dark: 'github-dark',\n },\n defaultColor: false,\n });\n this.highlightedHtml.set(this.sanitizer.bypassSecurityTrustHtml(html));\n } catch {\n this.highlightedHtml.set(null);\n }\n }\n\n focusTextarea(): void {\n if (!this.disabled() && !this.readonly()) {\n this.textarea().nativeElement.focus();\n }\n }\n\n protected onInput(event: Event): void {\n const target = event.target as HTMLTextAreaElement;\n this.value.set(target.value);\n this.valueChange.emit(target.value);\n this.updateCursorPosition(target);\n }\n\n protected onKeydown(event: KeyboardEvent): void {\n const target = event.target as HTMLTextAreaElement;\n\n // Handle Tab key\n if (event.key === 'Tab') {\n event.preventDefault();\n const start = target.selectionStart;\n const end = target.selectionEnd;\n const value = target.value;\n\n const indent = this.insertSpaces() ? ' '.repeat(this.tabSize()) : '\\t';\n\n if (event.shiftKey) {\n // Outdent\n const lineStart = value.lastIndexOf('\\n', start - 1) + 1;\n const lineContent = value.slice(lineStart, start);\n const indentMatch = lineContent.match(/^(\\t| {1,})/);\n\n if (indentMatch) {\n const removeLength = Math.min(\n indentMatch[1].length,\n this.insertSpaces() ? this.tabSize() : 1,\n );\n const newValue =\n value.slice(0, lineStart) + value.slice(lineStart + removeLength);\n\n this.value.set(newValue);\n this.valueChange.emit(newValue);\n\n // Restore cursor position\n setTimeout(() => {\n target.selectionStart = target.selectionEnd = start - removeLength;\n });\n }\n } else {\n // Indent\n const newValue = value.slice(0, start) + indent + value.slice(end);\n this.value.set(newValue);\n this.valueChange.emit(newValue);\n\n // Move cursor after indent\n setTimeout(() => {\n target.selectionStart = target.selectionEnd = start + indent.length;\n });\n }\n }\n\n // Handle Enter for auto-indent\n if (event.key === 'Enter' && !event.shiftKey) {\n const start = target.selectionStart;\n const value = target.value;\n\n // Find the current line's indentation\n const lineStart = value.lastIndexOf('\\n', start - 1) + 1;\n const lineContent = value.slice(lineStart, start);\n const indentMatch = lineContent.match(/^(\\s*)/);\n const currentIndent = indentMatch ? indentMatch[1] : '';\n\n // Check if we need extra indent (after { or :)\n const charBefore = value[start - 1];\n const extraIndent =\n charBefore === '{' || charBefore === ':' || charBefore === '['\n ? this.insertSpaces()\n ? ' '.repeat(this.tabSize())\n : '\\t'\n : '';\n\n event.preventDefault();\n const newValue =\n value.slice(0, start) +\n '\\n' +\n currentIndent +\n extraIndent +\n value.slice(start);\n\n this.value.set(newValue);\n this.valueChange.emit(newValue);\n\n setTimeout(() => {\n const newPos = start + 1 + currentIndent.length + extraIndent.length;\n target.selectionStart = target.selectionEnd = newPos;\n this.updateCursorPosition(target);\n });\n }\n\n // Handle closing brackets\n if (event.key === '}' || event.key === ']' || event.key === ')') {\n const start = target.selectionStart;\n const value = target.value;\n const lineStart = value.lastIndexOf('\\n', start - 1) + 1;\n const beforeCursor = value.slice(lineStart, start);\n\n // If line is only whitespace, reduce indent\n if (/^\\s+$/.test(beforeCursor)) {\n const reduceBy = this.insertSpaces() ? this.tabSize() : 1;\n const newIndent = beforeCursor.slice(\n 0,\n Math.max(0, beforeCursor.length - reduceBy),\n );\n\n event.preventDefault();\n const newValue =\n value.slice(0, lineStart) +\n newIndent +\n event.key +\n value.slice(start);\n\n this.value.set(newValue);\n this.valueChange.emit(newValue);\n\n setTimeout(() => {\n target.selectionStart = target.selectionEnd =\n lineStart + newIndent.length + 1;\n });\n }\n }\n }\n\n protected onScroll(event: Event): void {\n const target = event.target as HTMLTextAreaElement;\n this.scrollTop = target.scrollTop;\n this.scrollLeft = target.scrollLeft;\n\n // Sync scroll with the display layer\n const displayLayer = target.previousElementSibling as HTMLElement;\n if (displayLayer) {\n displayLayer.scrollTop = this.scrollTop;\n displayLayer.scrollLeft = this.scrollLeft;\n }\n }\n\n protected onFocus(): void {\n this.isFocused.set(true);\n }\n\n protected onBlur(): void {\n this.isFocused.set(false);\n }\n\n protected updateActiveLine(event: Event): void {\n const target = event.target as HTMLTextAreaElement;\n this.updateCursorPosition(target);\n }\n\n private updateCursorPosition(textarea: HTMLTextAreaElement): void {\n const value = textarea.value;\n const pos = textarea.selectionStart;\n\n // Calculate line and column\n const textBeforeCursor = value.slice(0, pos);\n const lines = textBeforeCursor.split('\\n');\n const line = lines.length;\n const column = lines[lines.length - 1].length + 1;\n\n this.activeLine.set(line);\n this.activeColumn.set(column);\n this.cursorChange.emit({ line, column });\n }\n}\n"]}
@@ -0,0 +1,90 @@
1
+ import { ChangeDetectionStrategy, Component, computed, input, signal, ViewEncapsulation, } from '@angular/core';
2
+ import { cn } from '@semantic-components/ui';
3
+ import * as i0 from "@angular/core";
4
+ export class ScCodeEditorCopyButton {
5
+ code = input.required(...(ngDevMode ? [{ debugName: "code" }] : []));
6
+ classInput = input('', { ...(ngDevMode ? { debugName: "classInput" } : {}), alias: 'class' });
7
+ copied = signal(false, ...(ngDevMode ? [{ debugName: "copied" }] : []));
8
+ class = computed(() => cn('rounded p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground', this.classInput()), ...(ngDevMode ? [{ debugName: "class" }] : []));
9
+ ariaLabel = computed(() => this.copied() ? 'Copied!' : 'Copy code', ...(ngDevMode ? [{ debugName: "ariaLabel" }] : []));
10
+ async copyCode(event) {
11
+ event.stopPropagation();
12
+ try {
13
+ await navigator.clipboard.writeText(this.code());
14
+ this.copied.set(true);
15
+ setTimeout(() => this.copied.set(false), 2000);
16
+ }
17
+ catch (err) {
18
+ console.error('Failed to copy code:', err);
19
+ }
20
+ }
21
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ScCodeEditorCopyButton, deps: [], target: i0.ɵɵFactoryTarget.Component });
22
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.4", type: ScCodeEditorCopyButton, isStandalone: true, selector: "button[sc-code-editor-copy-button]", inputs: { code: { classPropertyName: "code", publicName: "code", isSignal: true, isRequired: true, transformFunction: null }, classInput: { classPropertyName: "classInput", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "code-editor-copy-button", "type": "button" }, listeners: { "click": "copyCode($event)" }, properties: { "class": "class()", "attr.aria-label": "ariaLabel()" } }, ngImport: i0, template: `
23
+ @if (copied()) {
24
+ <svg
25
+ xmlns="http://www.w3.org/2000/svg"
26
+ viewBox="0 0 24 24"
27
+ fill="none"
28
+ stroke="currentColor"
29
+ stroke-width="2"
30
+ class="size-4"
31
+ >
32
+ <polyline points="20 6 9 17 4 12" />
33
+ </svg>
34
+ } @else {
35
+ <svg
36
+ xmlns="http://www.w3.org/2000/svg"
37
+ viewBox="0 0 24 24"
38
+ fill="none"
39
+ stroke="currentColor"
40
+ stroke-width="2"
41
+ class="size-4"
42
+ >
43
+ <rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
44
+ <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
45
+ </svg>
46
+ }
47
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
48
+ }
49
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ScCodeEditorCopyButton, decorators: [{
50
+ type: Component,
51
+ args: [{
52
+ selector: 'button[sc-code-editor-copy-button]',
53
+ template: `
54
+ @if (copied()) {
55
+ <svg
56
+ xmlns="http://www.w3.org/2000/svg"
57
+ viewBox="0 0 24 24"
58
+ fill="none"
59
+ stroke="currentColor"
60
+ stroke-width="2"
61
+ class="size-4"
62
+ >
63
+ <polyline points="20 6 9 17 4 12" />
64
+ </svg>
65
+ } @else {
66
+ <svg
67
+ xmlns="http://www.w3.org/2000/svg"
68
+ viewBox="0 0 24 24"
69
+ fill="none"
70
+ stroke="currentColor"
71
+ stroke-width="2"
72
+ class="size-4"
73
+ >
74
+ <rect width="14" height="14" x="8" y="8" rx="2" ry="2" />
75
+ <path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2" />
76
+ </svg>
77
+ }
78
+ `,
79
+ host: {
80
+ 'data-slot': 'code-editor-copy-button',
81
+ '[class]': 'class()',
82
+ '[attr.aria-label]': 'ariaLabel()',
83
+ type: 'button',
84
+ '(click)': 'copyCode($event)',
85
+ },
86
+ encapsulation: ViewEncapsulation.None,
87
+ changeDetection: ChangeDetectionStrategy.OnPush,
88
+ }]
89
+ }], propDecorators: { code: [{ type: i0.Input, args: [{ isSignal: true, alias: "code", required: true }] }], classInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
90
+ //# sourceMappingURL=code-editor-copy-button.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor-copy-button.js","sourceRoot":"","sources":["../../../../../../../libs/code/src/lib/components/code-editor/code-editor-copy-button.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,SAAS,EACT,QAAQ,EACR,KAAK,EACL,MAAM,EACN,iBAAiB,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,EAAE,EAAE,MAAM,yBAAyB,CAAC;;AAwC7C,MAAM,OAAO,sBAAsB;IACxB,IAAI,GAAG,KAAK,CAAC,QAAQ,+CAAU,CAAC;IAChC,UAAU,GAAG,KAAK,CAAS,EAAE,uDAAI,KAAK,EAAE,OAAO,GAAG,CAAC;IAEnD,MAAM,GAAG,MAAM,CAAC,KAAK,kDAAC,CAAC;IAEb,KAAK,GAAG,QAAQ,CAAC,GAAG,EAAE,CACvC,EAAE,CACA,6FAA6F,EAC7F,IAAI,CAAC,UAAU,EAAE,CAClB,iDACF,CAAC;IAEiB,SAAS,GAAG,QAAQ,CAAC,GAAG,EAAE,CAC3C,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,WAAW,qDACxC,CAAC;IAEQ,KAAK,CAAC,QAAQ,CAAC,KAAY;QACnC,KAAK,CAAC,eAAe,EAAE,CAAC;QAExB,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;YACjD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YACtB,UAAU,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CAAC,sBAAsB,EAAE,GAAG,CAAC,CAAC;QAC7C,CAAC;IACH,CAAC;uGA3BU,sBAAsB;2FAAtB,sBAAsB,siBApCvB;;;;;;;;;;;;;;;;;;;;;;;;;GAyBT;;2FAWU,sBAAsB;kBAtClC,SAAS;mBAAC;oBACT,QAAQ,EAAE,oCAAoC;oBAC9C,QAAQ,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBT;oBACD,IAAI,EAAE;wBACJ,WAAW,EAAE,yBAAyB;wBACtC,SAAS,EAAE,SAAS;wBACpB,mBAAmB,EAAE,aAAa;wBAClC,IAAI,EAAE,QAAQ;wBACd,SAAS,EAAE,kBAAkB;qBAC9B;oBACD,aAAa,EAAE,iBAAiB,CAAC,IAAI;oBACrC,eAAe,EAAE,uBAAuB,CAAC,MAAM;iBAChD","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n signal,\n ViewEncapsulation,\n} from '@angular/core';\nimport { cn } from '@semantic-components/ui';\n\n@Component({\n selector: 'button[sc-code-editor-copy-button]',\n template: `\n @if (copied()) {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n class=\"size-4\"\n >\n <polyline points=\"20 6 9 17 4 12\" />\n </svg>\n } @else {\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n stroke-width=\"2\"\n class=\"size-4\"\n >\n <rect width=\"14\" height=\"14\" x=\"8\" y=\"8\" rx=\"2\" ry=\"2\" />\n <path d=\"M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2\" />\n </svg>\n }\n `,\n host: {\n 'data-slot': 'code-editor-copy-button',\n '[class]': 'class()',\n '[attr.aria-label]': 'ariaLabel()',\n type: 'button',\n '(click)': 'copyCode($event)',\n },\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ScCodeEditorCopyButton {\n readonly code = input.required<string>();\n readonly classInput = input<string>('', { alias: 'class' });\n\n readonly copied = signal(false);\n\n protected readonly class = computed(() =>\n cn(\n 'rounded p-1.5 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground',\n this.classInput(),\n ),\n );\n\n protected readonly ariaLabel = computed(() =>\n this.copied() ? 'Copied!' : 'Copy code',\n );\n\n protected async copyCode(event: Event): Promise<void> {\n event.stopPropagation();\n\n try {\n await navigator.clipboard.writeText(this.code());\n this.copied.set(true);\n setTimeout(() => this.copied.set(false), 2000);\n } catch (err) {\n console.error('Failed to copy code:', err);\n }\n }\n}\n"]}
@@ -0,0 +1,27 @@
1
+ import { ChangeDetectionStrategy, Component, computed, input, ViewEncapsulation, } from '@angular/core';
2
+ import { cn } from '@semantic-components/ui';
3
+ import * as i0 from "@angular/core";
4
+ export class ScCodeEditorFooter {
5
+ classInput = input('', { ...(ngDevMode ? { debugName: "classInput" } : {}), alias: 'class' });
6
+ class = computed(() => cn('flex items-center justify-between border-t border-border bg-background/50 px-3 py-1.5 text-xs text-muted-foreground', this.classInput()), ...(ngDevMode ? [{ debugName: "class" }] : []));
7
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ScCodeEditorFooter, deps: [], target: i0.ɵɵFactoryTarget.Component });
8
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.4", type: ScCodeEditorFooter, isStandalone: true, selector: "div[sc-code-editor-footer]", inputs: { classInput: { classPropertyName: "classInput", publicName: "class", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "data-slot": "code-editor-footer" }, properties: { "class": "class()" } }, ngImport: i0, template: `
9
+ <ng-content />
10
+ `, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
11
+ }
12
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.4", ngImport: i0, type: ScCodeEditorFooter, decorators: [{
13
+ type: Component,
14
+ args: [{
15
+ selector: 'div[sc-code-editor-footer]',
16
+ template: `
17
+ <ng-content />
18
+ `,
19
+ host: {
20
+ 'data-slot': 'code-editor-footer',
21
+ '[class]': 'class()',
22
+ },
23
+ encapsulation: ViewEncapsulation.None,
24
+ changeDetection: ChangeDetectionStrategy.OnPush,
25
+ }]
26
+ }], propDecorators: { classInput: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }] } });
27
+ //# sourceMappingURL=code-editor-footer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"code-editor-footer.js","sourceRoot":"","sources":["../../../../../../../libs/code/src/lib/components/code-editor/code-editor-footer.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,uBAAuB,EACvB,SAAS,EACT,QAAQ,EACR,KAAK,EACL,iBAAiB,GAClB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,EAAE,EAAE,MAAM,yBAAyB,CAAC;;AAc7C,MAAM,OAAO,kBAAkB;IACpB,UAAU,GAAG,KAAK,CAAS,EAAE,uDAAI,KAAK,EAAE,OAAO,GAAG,CAAC;IAEzC,KAAK,GAAG,QAAQ,CAAC,GAAG,EAAE,CACvC,EAAE,CACA,qHAAqH,EACrH,IAAI,CAAC,UAAU,EAAE,CAClB,iDACF,CAAC;uGARS,kBAAkB;2FAAlB,kBAAkB,qUAVnB;;GAET;;2FAQU,kBAAkB;kBAZ9B,SAAS;mBAAC;oBACT,QAAQ,EAAE,4BAA4B;oBACtC,QAAQ,EAAE;;GAET;oBACD,IAAI,EAAE;wBACJ,WAAW,EAAE,oBAAoB;wBACjC,SAAS,EAAE,SAAS;qBACrB;oBACD,aAAa,EAAE,iBAAiB,CAAC,IAAI;oBACrC,eAAe,EAAE,uBAAuB,CAAC,MAAM;iBAChD","sourcesContent":["import {\n ChangeDetectionStrategy,\n Component,\n computed,\n input,\n ViewEncapsulation,\n} from '@angular/core';\nimport { cn } from '@semantic-components/ui';\n\n@Component({\n selector: 'div[sc-code-editor-footer]',\n template: `\n <ng-content />\n `,\n host: {\n 'data-slot': 'code-editor-footer',\n '[class]': 'class()',\n },\n encapsulation: ViewEncapsulation.None,\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class ScCodeEditorFooter {\n readonly classInput = input<string>('', { alias: 'class' });\n\n protected readonly class = computed(() =>\n cn(\n 'flex items-center justify-between border-t border-border bg-background/50 px-3 py-1.5 text-xs text-muted-foreground',\n this.classInput(),\n ),\n );\n}\n"]}