@particle-academy/fancy-code 0.15.1 → 0.17.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/dist/index.d.cts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { RefObject, ReactNode, CSSProperties, ComponentType } from 'react';
3
- import { ThemeColors, DiffAnnotations, LineChangeType, Token, Tokenizer } from '@particle-academy/fancy-file-commons';
4
- export { ThemeColors, Token, TokenType, Tokenizer, languageFromFilename } from '@particle-academy/fancy-file-commons';
3
+ import { ThemeColors, DiffAnnotations, LineChangeType, Tokenizer } from '@particle-academy/fancy-file-commons';
4
+ export { ThemeColors, Token, TokenType, Tokenizer, languageFromFilename, tokenizeMarkdown } from '@particle-academy/fancy-file-commons';
5
5
  import { MediaKind, MediaViewerProps, FauxClientProps, InputTagProps, InputTagTriggers, InputTagAdapter } from '@particle-academy/react-fancy';
6
6
 
7
7
  interface ThemeDefinition {
@@ -21,6 +21,11 @@ interface UseEditorEngineReturn {
21
21
  themeColors: ThemeColors;
22
22
  handleKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
23
23
  handleInput: () => void;
24
+ /**
25
+ * Ends the focus session: disarms Tab-indent so the next Tab into the editor
26
+ * moves focus rather than indenting. Wire to the textarea's `onBlur`.
27
+ */
28
+ handleBlur: () => void;
24
29
  handleScroll: () => void;
25
30
  handleSelect: () => void;
26
31
  /**
@@ -49,13 +54,22 @@ interface CodeEditorProps {
49
54
  onLanguageChange?: (lang: string) => void;
50
55
  /** Theme name: "light", "dark", "auto", or a custom registered name (default: "auto") */
51
56
  theme?: string;
52
- /** Prevent editing (default: false) */
57
+ /**
58
+ * Prevent editing (default: false). Read-only editors still take every
59
+ * navigation key, and Tab / Shift+Tab move focus out.
60
+ */
53
61
  readOnly?: boolean;
54
62
  /** Show line numbers (default: true) */
55
63
  lineNumbers?: boolean;
56
64
  /** Enable word wrap (default: false) */
57
65
  wordWrap?: boolean;
58
- /** Tab size in spaces (default: 2) */
66
+ /**
67
+ * Tab size in spaces (default: 2).
68
+ *
69
+ * Tab / Shift+Tab indent and dedent only once the user has edited in the
70
+ * current focus session; until then Tab moves focus like any other control,
71
+ * so keyboard users are never trapped (WCAG 2.1.2). Escape and blur disarm.
72
+ */
59
73
  tabSize?: number;
60
74
  /** Placeholder shown when empty */
61
75
  placeholder?: string;
@@ -88,7 +102,19 @@ interface CodeEditorProps {
88
102
  * marks. Requires `lineNumbers`.
89
103
  */
90
104
  diffBase?: string | null;
105
+ /**
106
+ * Accessible name of the editing surface. Lands on the `<textarea>` a person
107
+ * types into, not on a wrapper. Default "Code editor"; ignored when
108
+ * `aria-labelledby` is set.
109
+ */
110
+ "aria-label"?: string;
111
+ /** Id(s) of the element(s) naming the editing surface. Wins over `aria-label`. */
112
+ "aria-labelledby"?: string;
113
+ /** Id(s) of the element(s) describing the editing surface. */
114
+ "aria-describedby"?: string;
91
115
  }
116
+ /** The accessible-name props every editor forwards to its textarea. */
117
+ type EditorAriaProps = Pick<CodeEditorProps, "aria-label" | "aria-labelledby" | "aria-describedby">;
92
118
  interface CodeEditorContextValue {
93
119
  /** Get the current document text */
94
120
  getValue: () => string;
@@ -166,6 +192,12 @@ interface CodeEditorContextValue {
166
192
  _maxHeight?: number;
167
193
  /** @internal — lines to band; see {@link CodeEditorProps.highlightLines}. */
168
194
  _highlightLines?: readonly number[];
195
+ /** @internal — accessible-name attributes for the textarea. */
196
+ _aria?: {
197
+ "aria-label"?: string;
198
+ "aria-labelledby"?: string;
199
+ "aria-describedby"?: string;
200
+ };
169
201
  }
170
202
  interface CodeEditorToolbarProps {
171
203
  children?: ReactNode;
@@ -199,7 +231,7 @@ declare namespace CodeEditorStatusBar {
199
231
  var displayName: string;
200
232
  }
201
233
 
202
- declare function CodeEditorRoot({ children, className, value: valueProp, defaultValue, onChange, language: languageProp, onLanguageChange, theme, readOnly, lineNumbers: lineNumbersProp, wordWrap: wordWrapProp, tabSize: tabSizeProp, placeholder, minHeight, maxHeight, cursorLine, cursorColumn, diffBase, highlightLines, }: CodeEditorProps): react.JSX.Element;
234
+ declare function CodeEditorRoot({ children, className, value: valueProp, defaultValue, onChange, language: languageProp, onLanguageChange, theme, readOnly, lineNumbers: lineNumbersProp, wordWrap: wordWrapProp, tabSize: tabSizeProp, placeholder, minHeight, maxHeight, cursorLine, cursorColumn, diffBase, highlightLines, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, }: CodeEditorProps): react.JSX.Element;
203
235
  declare namespace CodeEditorRoot {
204
236
  var displayName: string;
205
237
  }
@@ -214,7 +246,11 @@ declare const CodeEditor: typeof CodeEditorRoot & {
214
246
  declare function useCodeEditor(): CodeEditorContextValue;
215
247
 
216
248
  type MarkdownEditorMode = "split" | "edit" | "preview";
217
- interface MarkdownEditorProps {
249
+ /**
250
+ * `aria-label` / `aria-labelledby` / `aria-describedby` name the editing
251
+ * textarea. Default name: "Markdown editor".
252
+ */
253
+ interface MarkdownEditorProps extends EditorAriaProps {
218
254
  /** Controlled markdown value. */
219
255
  value?: string;
220
256
  /** Initial value (uncontrolled). */
@@ -265,7 +301,7 @@ interface MarkdownEditorProps {
265
301
  * <MarkdownEditor value={md} onValueChange={setMd} mode="split" minHeight={240} />
266
302
  * ```
267
303
  */
268
- declare function MarkdownEditor({ value, defaultValue, onValueChange, mode, theme, readOnly, lineNumbers, wordWrap, placeholder, minHeight, maxHeight, className, renderPreview, }: MarkdownEditorProps): react.JSX.Element;
304
+ declare function MarkdownEditor({ value, defaultValue, onValueChange, mode, theme, readOnly, lineNumbers, wordWrap, placeholder, minHeight, maxHeight, className, renderPreview, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, }: MarkdownEditorProps): react.JSX.Element;
269
305
  declare namespace MarkdownEditor {
270
306
  var displayName: string;
271
307
  }
@@ -319,7 +355,11 @@ interface ResolveFileKindInput {
319
355
  */
320
356
  declare function resolveFileKind({ filename, mime }: ResolveFileKindInput): FileKind;
321
357
 
322
- interface FileViewerProps {
358
+ /**
359
+ * `aria-label` / `aria-labelledby` / `aria-describedby` name the source view's
360
+ * textarea. Default name: the `filename`, else "File contents".
361
+ */
362
+ interface FileViewerProps extends EditorAriaProps {
323
363
  /**
324
364
  * File name or path — used to detect the file kind / editor language, and as
325
365
  * a label / alt text. Pass `mime` too when you have it (it's preferred).
@@ -405,7 +445,7 @@ interface FileViewerProps {
405
445
  * <FileViewer filename="logo.png" src={url} style={{ height: 360 }} />
406
446
  * ```
407
447
  */
408
- declare function FileViewer({ filename, mime, src, value, onChange, readOnly, theme, fileKind, lineNumbers, wordWrap, line, column, highlightLines, minHeight, maxHeight, mediaProps, view, frameProps, document, className, style, }: FileViewerProps): react.JSX.Element;
448
+ declare function FileViewer({ filename, mime, src, value, onChange, readOnly, theme, fileKind, lineNumbers, wordWrap, line, column, highlightLines, minHeight, maxHeight, mediaProps, view, frameProps, document, className, style, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, }: FileViewerProps): react.JSX.Element;
409
449
  declare namespace FileViewer {
410
450
  var displayName: string;
411
451
  }
@@ -485,14 +525,6 @@ declare function diffRemovedColor(colors: ThemeColors): string;
485
525
  */
486
526
  declare function renderMarkdown(markdown: string): string;
487
527
 
488
- /**
489
- * Minimal, dependency-free Markdown tokenizer for editor highlighting. Line-based
490
- * for block constructs (fences, headings, blockquotes, list markers) plus a few
491
- * safe, non-overlapping inline spans (code, bold/italic, links). Good enough to
492
- * read structure at a glance — not a full CommonMark parse.
493
- */
494
- declare function tokenizeMarkdown(source: string): Token[];
495
-
496
528
  interface LanguageDefinition {
497
529
  /** Display name shown in UI (e.g., "JavaScript") */
498
530
  name: string;
@@ -551,4 +583,4 @@ interface CodeEditorInputTagProps extends Omit<InputTagProps, "adapter"> {
551
583
  */
552
584
  declare function CodeEditorInputTag(props: CodeEditorInputTagProps): react.JSX.Element;
553
585
 
554
- export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type DocumentFormat, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type OfficeKind, type OfficeViewerOptions, type OfficeViewerProps, type ResolveFileKindInput, type ThemeDefinition, clearOfficeViewers, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, officeViewerFor, registerLanguage, registerOfficeViewer, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
586
+ export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type DocumentFormat, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type OfficeKind, type OfficeViewerOptions, type OfficeViewerProps, type ResolveFileKindInput, type ThemeDefinition, clearOfficeViewers, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, officeViewerFor, registerLanguage, registerOfficeViewer, registerTheme, renderMarkdown, resolveFileKind, useCodeEditor, useCodeEditorInputTagAdapter };
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as react from 'react';
2
2
  import { RefObject, ReactNode, CSSProperties, ComponentType } from 'react';
3
- import { ThemeColors, DiffAnnotations, LineChangeType, Token, Tokenizer } from '@particle-academy/fancy-file-commons';
4
- export { ThemeColors, Token, TokenType, Tokenizer, languageFromFilename } from '@particle-academy/fancy-file-commons';
3
+ import { ThemeColors, DiffAnnotations, LineChangeType, Tokenizer } from '@particle-academy/fancy-file-commons';
4
+ export { ThemeColors, Token, TokenType, Tokenizer, languageFromFilename, tokenizeMarkdown } from '@particle-academy/fancy-file-commons';
5
5
  import { MediaKind, MediaViewerProps, FauxClientProps, InputTagProps, InputTagTriggers, InputTagAdapter } from '@particle-academy/react-fancy';
6
6
 
7
7
  interface ThemeDefinition {
@@ -21,6 +21,11 @@ interface UseEditorEngineReturn {
21
21
  themeColors: ThemeColors;
22
22
  handleKeyDown: (e: React.KeyboardEvent<HTMLTextAreaElement>) => void;
23
23
  handleInput: () => void;
24
+ /**
25
+ * Ends the focus session: disarms Tab-indent so the next Tab into the editor
26
+ * moves focus rather than indenting. Wire to the textarea's `onBlur`.
27
+ */
28
+ handleBlur: () => void;
24
29
  handleScroll: () => void;
25
30
  handleSelect: () => void;
26
31
  /**
@@ -49,13 +54,22 @@ interface CodeEditorProps {
49
54
  onLanguageChange?: (lang: string) => void;
50
55
  /** Theme name: "light", "dark", "auto", or a custom registered name (default: "auto") */
51
56
  theme?: string;
52
- /** Prevent editing (default: false) */
57
+ /**
58
+ * Prevent editing (default: false). Read-only editors still take every
59
+ * navigation key, and Tab / Shift+Tab move focus out.
60
+ */
53
61
  readOnly?: boolean;
54
62
  /** Show line numbers (default: true) */
55
63
  lineNumbers?: boolean;
56
64
  /** Enable word wrap (default: false) */
57
65
  wordWrap?: boolean;
58
- /** Tab size in spaces (default: 2) */
66
+ /**
67
+ * Tab size in spaces (default: 2).
68
+ *
69
+ * Tab / Shift+Tab indent and dedent only once the user has edited in the
70
+ * current focus session; until then Tab moves focus like any other control,
71
+ * so keyboard users are never trapped (WCAG 2.1.2). Escape and blur disarm.
72
+ */
59
73
  tabSize?: number;
60
74
  /** Placeholder shown when empty */
61
75
  placeholder?: string;
@@ -88,7 +102,19 @@ interface CodeEditorProps {
88
102
  * marks. Requires `lineNumbers`.
89
103
  */
90
104
  diffBase?: string | null;
105
+ /**
106
+ * Accessible name of the editing surface. Lands on the `<textarea>` a person
107
+ * types into, not on a wrapper. Default "Code editor"; ignored when
108
+ * `aria-labelledby` is set.
109
+ */
110
+ "aria-label"?: string;
111
+ /** Id(s) of the element(s) naming the editing surface. Wins over `aria-label`. */
112
+ "aria-labelledby"?: string;
113
+ /** Id(s) of the element(s) describing the editing surface. */
114
+ "aria-describedby"?: string;
91
115
  }
116
+ /** The accessible-name props every editor forwards to its textarea. */
117
+ type EditorAriaProps = Pick<CodeEditorProps, "aria-label" | "aria-labelledby" | "aria-describedby">;
92
118
  interface CodeEditorContextValue {
93
119
  /** Get the current document text */
94
120
  getValue: () => string;
@@ -166,6 +192,12 @@ interface CodeEditorContextValue {
166
192
  _maxHeight?: number;
167
193
  /** @internal — lines to band; see {@link CodeEditorProps.highlightLines}. */
168
194
  _highlightLines?: readonly number[];
195
+ /** @internal — accessible-name attributes for the textarea. */
196
+ _aria?: {
197
+ "aria-label"?: string;
198
+ "aria-labelledby"?: string;
199
+ "aria-describedby"?: string;
200
+ };
169
201
  }
170
202
  interface CodeEditorToolbarProps {
171
203
  children?: ReactNode;
@@ -199,7 +231,7 @@ declare namespace CodeEditorStatusBar {
199
231
  var displayName: string;
200
232
  }
201
233
 
202
- declare function CodeEditorRoot({ children, className, value: valueProp, defaultValue, onChange, language: languageProp, onLanguageChange, theme, readOnly, lineNumbers: lineNumbersProp, wordWrap: wordWrapProp, tabSize: tabSizeProp, placeholder, minHeight, maxHeight, cursorLine, cursorColumn, diffBase, highlightLines, }: CodeEditorProps): react.JSX.Element;
234
+ declare function CodeEditorRoot({ children, className, value: valueProp, defaultValue, onChange, language: languageProp, onLanguageChange, theme, readOnly, lineNumbers: lineNumbersProp, wordWrap: wordWrapProp, tabSize: tabSizeProp, placeholder, minHeight, maxHeight, cursorLine, cursorColumn, diffBase, highlightLines, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, }: CodeEditorProps): react.JSX.Element;
203
235
  declare namespace CodeEditorRoot {
204
236
  var displayName: string;
205
237
  }
@@ -214,7 +246,11 @@ declare const CodeEditor: typeof CodeEditorRoot & {
214
246
  declare function useCodeEditor(): CodeEditorContextValue;
215
247
 
216
248
  type MarkdownEditorMode = "split" | "edit" | "preview";
217
- interface MarkdownEditorProps {
249
+ /**
250
+ * `aria-label` / `aria-labelledby` / `aria-describedby` name the editing
251
+ * textarea. Default name: "Markdown editor".
252
+ */
253
+ interface MarkdownEditorProps extends EditorAriaProps {
218
254
  /** Controlled markdown value. */
219
255
  value?: string;
220
256
  /** Initial value (uncontrolled). */
@@ -265,7 +301,7 @@ interface MarkdownEditorProps {
265
301
  * <MarkdownEditor value={md} onValueChange={setMd} mode="split" minHeight={240} />
266
302
  * ```
267
303
  */
268
- declare function MarkdownEditor({ value, defaultValue, onValueChange, mode, theme, readOnly, lineNumbers, wordWrap, placeholder, minHeight, maxHeight, className, renderPreview, }: MarkdownEditorProps): react.JSX.Element;
304
+ declare function MarkdownEditor({ value, defaultValue, onValueChange, mode, theme, readOnly, lineNumbers, wordWrap, placeholder, minHeight, maxHeight, className, renderPreview, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, }: MarkdownEditorProps): react.JSX.Element;
269
305
  declare namespace MarkdownEditor {
270
306
  var displayName: string;
271
307
  }
@@ -319,7 +355,11 @@ interface ResolveFileKindInput {
319
355
  */
320
356
  declare function resolveFileKind({ filename, mime }: ResolveFileKindInput): FileKind;
321
357
 
322
- interface FileViewerProps {
358
+ /**
359
+ * `aria-label` / `aria-labelledby` / `aria-describedby` name the source view's
360
+ * textarea. Default name: the `filename`, else "File contents".
361
+ */
362
+ interface FileViewerProps extends EditorAriaProps {
323
363
  /**
324
364
  * File name or path — used to detect the file kind / editor language, and as
325
365
  * a label / alt text. Pass `mime` too when you have it (it's preferred).
@@ -405,7 +445,7 @@ interface FileViewerProps {
405
445
  * <FileViewer filename="logo.png" src={url} style={{ height: 360 }} />
406
446
  * ```
407
447
  */
408
- declare function FileViewer({ filename, mime, src, value, onChange, readOnly, theme, fileKind, lineNumbers, wordWrap, line, column, highlightLines, minHeight, maxHeight, mediaProps, view, frameProps, document, className, style, }: FileViewerProps): react.JSX.Element;
448
+ declare function FileViewer({ filename, mime, src, value, onChange, readOnly, theme, fileKind, lineNumbers, wordWrap, line, column, highlightLines, minHeight, maxHeight, mediaProps, view, frameProps, document, className, style, "aria-label": ariaLabel, "aria-labelledby": ariaLabelledBy, "aria-describedby": ariaDescribedBy, }: FileViewerProps): react.JSX.Element;
409
449
  declare namespace FileViewer {
410
450
  var displayName: string;
411
451
  }
@@ -485,14 +525,6 @@ declare function diffRemovedColor(colors: ThemeColors): string;
485
525
  */
486
526
  declare function renderMarkdown(markdown: string): string;
487
527
 
488
- /**
489
- * Minimal, dependency-free Markdown tokenizer for editor highlighting. Line-based
490
- * for block constructs (fences, headings, blockquotes, list markers) plus a few
491
- * safe, non-overlapping inline spans (code, bold/italic, links). Good enough to
492
- * read structure at a glance — not a full CommonMark parse.
493
- */
494
- declare function tokenizeMarkdown(source: string): Token[];
495
-
496
528
  interface LanguageDefinition {
497
529
  /** Display name shown in UI (e.g., "JavaScript") */
498
530
  name: string;
@@ -551,4 +583,4 @@ interface CodeEditorInputTagProps extends Omit<InputTagProps, "adapter"> {
551
583
  */
552
584
  declare function CodeEditorInputTag(props: CodeEditorInputTagProps): react.JSX.Element;
553
585
 
554
- export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type DocumentFormat, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type OfficeKind, type OfficeViewerOptions, type OfficeViewerProps, type ResolveFileKindInput, type ThemeDefinition, clearOfficeViewers, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, officeViewerFor, registerLanguage, registerOfficeViewer, registerTheme, renderMarkdown, resolveFileKind, tokenizeMarkdown, useCodeEditor, useCodeEditorInputTagAdapter };
586
+ export { CodeEditor, type CodeEditorContextValue, CodeEditorInputTag, type CodeEditorInputTagProps, type CodeEditorPanelProps, type CodeEditorProps, type CodeEditorStatusBarProps, type CodeEditorToolbarProps, type DocumentFormat, type FileKind, FileViewer, type FileViewerProps, type GutterDiffMark, type LanguageDefinition, MarkdownEditor, type MarkdownEditorMode, type MarkdownEditorProps, type OfficeKind, type OfficeViewerOptions, type OfficeViewerProps, type ResolveFileKindInput, type ThemeDefinition, clearOfficeViewers, diffMarkColor, diffRemovedColor, getLanguage, getRegisteredLanguages, getRegisteredThemes, getTheme, gutterDiffMark, hasDeletedAtEnd, officeViewerFor, registerLanguage, registerOfficeViewer, registerTheme, renderMarkdown, resolveFileKind, useCodeEditor, useCodeEditorInputTagAdapter };