mdsquare 0.1.0 → 0.2.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/README.md CHANGED
@@ -32,6 +32,17 @@ editor.getValue(); // 지금 문서 (마크다운)
32
32
  editor.setValue("# 새 문서");
33
33
  ```
34
34
 
35
+ ### 파일 열기
36
+
37
+ 파일을 읽고 저장하는 것은 앱이 맡고, 에디터에는 글자만 넘깁니다. 다른 파일로 바꿀 때는 `open`을 씁니다.
38
+ (`setValue`로 바꾸면 Ctrl+Z로 이전 파일 내용이 돌아오고, `onChange`가 불려 방금 연 파일을 바로 저장하게 됩니다)
39
+
40
+ ```ts
41
+ async function openFile(file: File) {
42
+ editor.open(await file.text());
43
+ }
44
+ ```
45
+
35
46
  에디터 높이는 CSS로 정합니다. (기본: 내용만큼 늘어남)
36
47
 
37
48
  ```css
@@ -65,7 +76,8 @@ editor.setValue("# 새 문서");
65
76
  | 메서드 | 설명 |
66
77
  |---|---|
67
78
  | `getValue()` | 지금 문서 |
68
- | `setValue(value)` | 문서 전체 바꾸기 (실행 취소 가능) |
79
+ | `open(value)` | 다른 파일(문서) 열기. 실행 취소 기록을 새로 시작하고 `onChange`를 부르지 않음. 커서는 처음, 스크롤은 맨 위 |
80
+ | `setValue(value)` | 지금 문서의 내용 바꾸기. 편집처럼 실행 취소할 수 있고 `onChange`도 부름 |
69
81
  | `focus()` | 에디터에 포커스 |
70
82
  | `setTheme(theme)` | 테마 바꾸기 |
71
83
  | `setReadOnly(readOnly)` | 읽기 전용 바꾸기 |
@@ -124,9 +136,9 @@ TikZ를 켜는 앱이 TikZJax 파일을 직접 제공하므로, 그 앱은 TikZJ
124
136
  5. [x] Highlight: `==`
125
137
  - [x] Inline Code
126
138
  - [x] Escape
127
- - [ ] Heading
139
+ - [x] Heading
128
140
  1. [x] Normal Heading: `#`
129
- 2. [ ] SetextHeading: `===`, `---` (보류: 스타일만 적용, 밑줄 줄 숨김 미구현)
141
+ 2. [x] SetextHeading(`===`, `---` 밑줄) 비활성: `===`는 글, 문단 바로 아래 `---`는 수평선. 켜려면 `markdown: { setextHeadings: true }` (확장으로 쓸 때는 `extendedMarkdown({ setextHeadings: true })`)
130
142
  - [x] HashTag
131
143
  1. [x] Hierarchical HashTag: `#parent/child`
132
144
  - [x] Mention
package/dist/editor.d.ts CHANGED
@@ -92,12 +92,30 @@ export declare class MDSquareEditor {
92
92
  private theme;
93
93
  private media;
94
94
  private readonly onMediaChange;
95
+ /** 옵션으로 정해지는 확장들. 파일을 새로 열 때도 같은 것을 써서 렌더링 캐시(수식, TikZ 등)를 이어 씁니다. */
96
+ private readonly extensions;
97
+ private readOnly;
95
98
  constructor(options?: MDSquareEditorOptions);
99
+ /** 지금 설정(테마, 읽기 전용)으로 새 상태를 만듭니다. */
100
+ private createState;
96
101
  /** 에디터 바깥 요소 (`.cm-editor`) */
97
102
  get dom(): HTMLElement;
98
103
  /** 지금 문서 (마크다운) */
99
104
  getValue(): string;
100
- /** 문서 전체를 바꿉니다. (실행 취소할 수 있고 onChange도 호출됨) 커서는 문서 처음으로 */
105
+ /**
106
+ * 다른 파일(문서)을 엽니다. 처음 연 것처럼 새로 시작합니다.
107
+ * - 실행 취소 기록을 새로 시작합니다. (Ctrl+Z로 이전 파일 내용으로 돌아가지 않음)
108
+ * - onChange를 부르지 않습니다. (방금 연 파일을 바로 저장하지 않음)
109
+ * - 커서는 문서 처음, 스크롤은 맨 위. 접기·열린 메뉴 등 화면 상태도 초기화합니다.
110
+ * - 테마, 읽기 전용 등 설정은 그대로입니다.
111
+ *
112
+ * 같은 문서 안에서 내용을 바꾸는 것(실행 취소할 수 있어야 하는 편집)은 setValue를 쓰세요.
113
+ */
114
+ open(value: string): void;
115
+ /**
116
+ * 지금 문서의 내용을 바꿉니다. 편집처럼 다뤄 실행 취소할 수 있고 onChange도 호출됩니다. 커서는 문서 처음으로.
117
+ * 다른 파일을 여는 것이라면 open을 쓰세요.
118
+ */
101
119
  setValue(value: string): void;
102
120
  focus(): void;
103
121
  /** 테마 바꾸기 ("auto": 시스템 설정을 따르고, 설정이 바뀌면 함께 바뀜) */
package/dist/editor.js CHANGED
@@ -70,32 +70,40 @@ export class MDSquareEditor {
70
70
  theme;
71
71
  media = null;
72
72
  onMediaChange = () => this.applyTheme();
73
+ /** 옵션으로 정해지는 확장들. 파일을 새로 열 때도 같은 것을 써서 렌더링 캐시(수식, TikZ 등)를 이어 씁니다. */
74
+ extensions;
75
+ readOnly;
73
76
  constructor(options = {}) {
74
77
  this.theme = options.theme ?? "auto";
78
+ this.readOnly = options.readOnly ?? false;
75
79
  const onChange = options.onChange;
76
- this.view = new EditorView({
77
- state: EditorState.create({
78
- doc: options.value ?? "",
79
- extensions: [
80
- basicSetup({ lineNumbers: options.lineNumbers ?? false }),
81
- EditorView.lineWrapping,
82
- this.themeSlot.of(this.themeExtension()),
83
- this.readOnlySlot.of(readOnlyExtension(options.readOnly ?? false)),
84
- options.placeholder ? placeholderExtension(options.placeholder) : [],
85
- markdownExtensions(options),
86
- onChange
87
- ? EditorView.updateListener.of((update) => {
88
- if (update.docChanged)
89
- onChange(update.state.doc.toString(), this);
90
- })
91
- : [],
92
- options.extensions ?? [],
93
- ],
94
- }),
95
- parent: options.parent,
96
- });
80
+ this.extensions = [
81
+ basicSetup({ lineNumbers: options.lineNumbers ?? false }),
82
+ EditorView.lineWrapping,
83
+ options.placeholder ? placeholderExtension(options.placeholder) : [],
84
+ markdownExtensions(options),
85
+ onChange
86
+ ? EditorView.updateListener.of((update) => {
87
+ if (update.docChanged)
88
+ onChange(update.state.doc.toString(), this);
89
+ })
90
+ : [],
91
+ options.extensions ?? [],
92
+ ];
93
+ this.view = new EditorView({ state: this.createState(options.value ?? ""), parent: options.parent });
97
94
  this.watchSystemTheme();
98
95
  }
96
+ /** 지금 설정(테마, 읽기 전용)으로 새 상태를 만듭니다. */
97
+ createState(doc) {
98
+ return EditorState.create({
99
+ doc,
100
+ extensions: [
101
+ this.themeSlot.of(this.themeExtension()),
102
+ this.readOnlySlot.of(readOnlyExtension(this.readOnly)),
103
+ this.extensions,
104
+ ],
105
+ });
106
+ }
99
107
  /** 에디터 바깥 요소 (`.cm-editor`) */
100
108
  get dom() {
101
109
  return this.view.dom;
@@ -104,7 +112,24 @@ export class MDSquareEditor {
104
112
  getValue() {
105
113
  return this.view.state.doc.toString();
106
114
  }
107
- /** 문서 전체를 바꿉니다. (실행 취소할 수 있고 onChange도 호출됨) 커서는 문서 처음으로 */
115
+ /**
116
+ * 다른 파일(문서)을 엽니다. 처음 연 것처럼 새로 시작합니다.
117
+ * - 실행 취소 기록을 새로 시작합니다. (Ctrl+Z로 이전 파일 내용으로 돌아가지 않음)
118
+ * - onChange를 부르지 않습니다. (방금 연 파일을 바로 저장하지 않음)
119
+ * - 커서는 문서 처음, 스크롤은 맨 위. 접기·열린 메뉴 등 화면 상태도 초기화합니다.
120
+ * - 테마, 읽기 전용 등 설정은 그대로입니다.
121
+ *
122
+ * 같은 문서 안에서 내용을 바꾸는 것(실행 취소할 수 있어야 하는 편집)은 setValue를 쓰세요.
123
+ */
124
+ open(value) {
125
+ this.view.setState(this.createState(value));
126
+ this.view.scrollDOM.scrollTop = 0;
127
+ this.view.scrollDOM.scrollLeft = 0;
128
+ }
129
+ /**
130
+ * 지금 문서의 내용을 바꿉니다. 편집처럼 다뤄 실행 취소할 수 있고 onChange도 호출됩니다. 커서는 문서 처음으로.
131
+ * 다른 파일을 여는 것이라면 open을 쓰세요.
132
+ */
108
133
  setValue(value) {
109
134
  this.view.dispatch({
110
135
  changes: { from: 0, to: this.view.state.doc.length, insert: value },
@@ -121,6 +146,7 @@ export class MDSquareEditor {
121
146
  this.applyTheme();
122
147
  }
123
148
  setReadOnly(readOnly) {
149
+ this.readOnly = readOnly;
124
150
  this.view.dispatch({ effects: this.readOnlySlot.reconfigure(readOnlyExtension(readOnly)) });
125
151
  }
126
152
  /** 에디터를 없애고 이벤트를 정리합니다. */
@@ -1,6 +1,6 @@
1
1
  import { markdown } from "@codemirror/lang-markdown";
2
2
  import { type Extension } from "@codemirror/state";
3
- import type { MarkdownExtension } from "@lezer/markdown";
3
+ import type { MarkdownConfig, MarkdownExtension } from "@lezer/markdown";
4
4
  import { Highlight, markTag } from "./highlight.js";
5
5
  import { Underline, underlineTag } from "./underline.js";
6
6
  import { Hashtag, hashtagTag } from "./hashtagSyntax.js";
@@ -58,7 +58,15 @@ export interface ExtendedMarkdownOptions {
58
58
  * 바로 위 항목의 이어지는 줄이 됩니다.
59
59
  */
60
60
  blankLineAfterList?: boolean;
61
+ /**
62
+ * Setext 제목(`제목` 다음 줄의 `===`, `---`)을 쓸지 여부. 기본값 false (쓰지 않음).
63
+ * 끄면 `===` 줄은 그냥 글이고, 문단 바로 아래의 `---`는 수평선입니다.
64
+ * (`---`가 의도와 달리 윗줄을 제목으로 만드는 일을 막습니다. 제목은 `#`를 씁니다)
65
+ */
66
+ setextHeadings?: boolean;
61
67
  }
68
+ /** Setext 제목(`===`, `---` 밑줄)을 끄는 문법 설정 */
69
+ export declare const NoSetextHeading: MarkdownConfig;
62
70
  /**
63
71
  * 확장 마크다운 문법(파서)을 활성화하는 CodeMirror 확장입니다.
64
72
  * 시각 스타일(하이라이트 배경, 기호 숨김 등)은 `emphasis()`가 담당합니다.
@@ -51,19 +51,21 @@ export const extendedMarkdownSyntax = [
51
51
  MathSyntax,
52
52
  TableSyntax,
53
53
  ];
54
+ /** Setext 제목(`===`, `---` 밑줄)을 끄는 문법 설정 */
55
+ export const NoSetextHeading = { remove: ["SetextHeading"] };
54
56
  /**
55
57
  * 확장 마크다운 문법(파서)을 활성화하는 CodeMirror 확장입니다.
56
58
  * 시각 스타일(하이라이트 배경, 기호 숨김 등)은 `emphasis()`가 담당합니다.
57
59
  */
58
60
  export function extendedMarkdown(options = {}) {
59
- const { markdownConfig = {}, nonTightLists = false, blankLineAfterList = true } = options;
61
+ const { markdownConfig = {}, nonTightLists = false, blankLineAfterList = true, setextHeadings = false } = options;
60
62
  const userExtensions = markdownConfig.extensions ?? [];
61
63
  const addKeymap = markdownConfig.addKeymap ?? true;
62
64
  return [
63
65
  markdown({
64
66
  base: markdownLanguage,
65
67
  ...markdownConfig,
66
- extensions: [extendedMarkdownSyntax, userExtensions],
68
+ extensions: [extendedMarkdownSyntax, setextHeadings ? [] : NoSetextHeading, userExtensions],
67
69
  // 기본 키맵은 Enter 동작을 바꿀 수 없으므로 끄고, 아래에서 같은 우선순위로 다시 등록합니다.
68
70
  addKeymap: false,
69
71
  }),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdsquare",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Hybrid (live preview) Markdown editor built on CodeMirror 6: tables, callouts, math, code blocks, footnotes and more",
5
5
  "keywords": [
6
6
  "markdown",