@milkdown/crepe 7.19.2 → 7.21.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/lib/cjs/builder.js +44 -1
- package/lib/cjs/builder.js.map +1 -1
- package/lib/cjs/feature/ai/index.js +1492 -0
- package/lib/cjs/feature/ai/index.js.map +1 -0
- package/lib/cjs/feature/block-edit/index.js +9 -2
- package/lib/cjs/feature/block-edit/index.js.map +1 -1
- package/lib/cjs/feature/code-mirror/index.js +2 -0
- package/lib/cjs/feature/code-mirror/index.js.map +1 -1
- package/lib/cjs/feature/cursor/index.js +2 -0
- package/lib/cjs/feature/cursor/index.js.map +1 -1
- package/lib/cjs/feature/image-block/index.js +5 -1
- package/lib/cjs/feature/image-block/index.js.map +1 -1
- package/lib/cjs/feature/latex/index.js +7 -0
- package/lib/cjs/feature/latex/index.js.map +1 -1
- package/lib/cjs/feature/link-tooltip/index.js +2 -0
- package/lib/cjs/feature/link-tooltip/index.js.map +1 -1
- package/lib/cjs/feature/list-item/index.js +2 -0
- package/lib/cjs/feature/list-item/index.js.map +1 -1
- package/lib/cjs/feature/placeholder/index.js +2 -0
- package/lib/cjs/feature/placeholder/index.js.map +1 -1
- package/lib/cjs/feature/table/index.js +2 -0
- package/lib/cjs/feature/table/index.js.map +1 -1
- package/lib/cjs/feature/toolbar/index.js +497 -5
- package/lib/cjs/feature/toolbar/index.js.map +1 -1
- package/lib/cjs/feature/top-bar/index.js +791 -0
- package/lib/cjs/feature/top-bar/index.js.map +1 -0
- package/lib/cjs/index.js +2047 -160
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/llm-providers/anthropic/index.js +147 -0
- package/lib/cjs/llm-providers/anthropic/index.js.map +1 -0
- package/lib/cjs/llm-providers/openai/index.js +138 -0
- package/lib/cjs/llm-providers/openai/index.js.map +1 -0
- package/lib/esm/builder.js +44 -1
- package/lib/esm/builder.js.map +1 -1
- package/lib/esm/feature/ai/index.js +1487 -0
- package/lib/esm/feature/ai/index.js.map +1 -0
- package/lib/esm/feature/block-edit/index.js +9 -2
- package/lib/esm/feature/block-edit/index.js.map +1 -1
- package/lib/esm/feature/code-mirror/index.js +2 -0
- package/lib/esm/feature/code-mirror/index.js.map +1 -1
- package/lib/esm/feature/cursor/index.js +2 -0
- package/lib/esm/feature/cursor/index.js.map +1 -1
- package/lib/esm/feature/image-block/index.js +5 -1
- package/lib/esm/feature/image-block/index.js.map +1 -1
- package/lib/esm/feature/latex/index.js +7 -0
- package/lib/esm/feature/latex/index.js.map +1 -1
- package/lib/esm/feature/link-tooltip/index.js +2 -0
- package/lib/esm/feature/link-tooltip/index.js.map +1 -1
- package/lib/esm/feature/list-item/index.js +2 -0
- package/lib/esm/feature/list-item/index.js.map +1 -1
- package/lib/esm/feature/placeholder/index.js +2 -0
- package/lib/esm/feature/placeholder/index.js.map +1 -1
- package/lib/esm/feature/table/index.js +2 -0
- package/lib/esm/feature/table/index.js.map +1 -1
- package/lib/esm/feature/toolbar/index.js +499 -7
- package/lib/esm/feature/toolbar/index.js.map +1 -1
- package/lib/esm/feature/top-bar/index.js +789 -0
- package/lib/esm/feature/top-bar/index.js.map +1 -0
- package/lib/esm/index.js +2040 -153
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/llm-providers/anthropic/index.js +145 -0
- package/lib/esm/llm-providers/anthropic/index.js.map +1 -0
- package/lib/esm/llm-providers/openai/index.js +136 -0
- package/lib/esm/llm-providers/openai/index.js.map +1 -0
- package/lib/theme/common/ai.css +446 -0
- package/lib/theme/common/code-mirror.css +14 -0
- package/lib/theme/common/diff.css +177 -0
- package/lib/theme/common/style.css +3 -0
- package/lib/theme/common/top-bar.css +152 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types/core/builder.d.ts +2 -1
- package/lib/types/core/builder.d.ts.map +1 -1
- package/lib/types/feature/ai/ai.spec.d.ts +2 -0
- package/lib/types/feature/ai/ai.spec.d.ts.map +1 -0
- package/lib/types/feature/ai/commands.d.ts +24 -0
- package/lib/types/feature/ai/commands.d.ts.map +1 -0
- package/lib/types/feature/ai/context.d.ts +4 -0
- package/lib/types/feature/ai/context.d.ts.map +1 -0
- package/lib/types/feature/ai/diff-actions/index.d.ts +12 -0
- package/lib/types/feature/ai/diff-actions/index.d.ts.map +1 -0
- package/lib/types/feature/ai/diff-actions/view.d.ts +21 -0
- package/lib/types/feature/ai/diff-actions/view.d.ts.map +1 -0
- package/lib/types/feature/ai/index.d.ts +7 -0
- package/lib/types/feature/ai/index.d.ts.map +1 -0
- package/lib/types/feature/ai/instruction-tooltip/component.d.ts +26 -0
- package/lib/types/feature/ai/instruction-tooltip/component.d.ts.map +1 -0
- package/lib/types/feature/ai/instruction-tooltip/index.d.ts +17 -0
- package/lib/types/feature/ai/instruction-tooltip/index.d.ts.map +1 -0
- package/lib/types/feature/ai/instruction-tooltip/suggestions.d.ts +50 -0
- package/lib/types/feature/ai/instruction-tooltip/suggestions.d.ts.map +1 -0
- package/lib/types/feature/ai/instruction-tooltip/view.d.ts +19 -0
- package/lib/types/feature/ai/instruction-tooltip/view.d.ts.map +1 -0
- package/lib/types/feature/ai/streaming-indicator.d.ts +9 -0
- package/lib/types/feature/ai/streaming-indicator.d.ts.map +1 -0
- package/lib/types/feature/ai/types.d.ts +58 -0
- package/lib/types/feature/ai/types.d.ts.map +1 -0
- package/lib/types/feature/block-edit/handle/component.d.ts.map +1 -1
- package/lib/types/feature/block-edit/menu/component.d.ts.map +1 -1
- package/lib/types/feature/image-block/index.d.ts +2 -0
- package/lib/types/feature/image-block/index.d.ts.map +1 -1
- package/lib/types/feature/index.d.ts +7 -1
- package/lib/types/feature/index.d.ts.map +1 -1
- package/lib/types/feature/latex/inline-tooltip/component.d.ts.map +1 -1
- package/lib/types/feature/latex/inline-tooltip/inline-tooltip.spec.d.ts +2 -0
- package/lib/types/feature/latex/inline-tooltip/inline-tooltip.spec.d.ts.map +1 -0
- package/lib/types/feature/latex/inline-tooltip/view.d.ts.map +1 -1
- package/lib/types/feature/loader.d.ts.map +1 -1
- package/lib/types/feature/toolbar/component.d.ts.map +1 -1
- package/lib/types/feature/toolbar/config.d.ts +1 -1
- package/lib/types/feature/toolbar/config.d.ts.map +1 -1
- package/lib/types/feature/toolbar/index.d.ts +1 -0
- package/lib/types/feature/toolbar/index.d.ts.map +1 -1
- package/lib/types/feature/top-bar/component.d.ts +11 -0
- package/lib/types/feature/top-bar/component.d.ts.map +1 -0
- package/lib/types/feature/top-bar/config.d.ts +34 -0
- package/lib/types/feature/top-bar/config.d.ts.map +1 -0
- package/lib/types/feature/top-bar/index.d.ts +26 -0
- package/lib/types/feature/top-bar/index.d.ts.map +1 -0
- package/lib/types/icons/ai.d.ts +2 -0
- package/lib/types/icons/ai.d.ts.map +1 -0
- package/lib/types/icons/chevron-left.d.ts +2 -0
- package/lib/types/icons/chevron-left.d.ts.map +1 -0
- package/lib/types/icons/chevron-right.d.ts +2 -0
- package/lib/types/icons/chevron-right.d.ts.map +1 -0
- package/lib/types/icons/code-block.d.ts +2 -0
- package/lib/types/icons/code-block.d.ts.map +1 -0
- package/lib/types/icons/enter-key.d.ts +2 -0
- package/lib/types/icons/enter-key.d.ts.map +1 -0
- package/lib/types/icons/grammar-check.d.ts +2 -0
- package/lib/types/icons/grammar-check.d.ts.map +1 -0
- package/lib/types/icons/index.d.ts +12 -0
- package/lib/types/icons/index.d.ts.map +1 -1
- package/lib/types/icons/longer.d.ts +2 -0
- package/lib/types/icons/longer.d.ts.map +1 -0
- package/lib/types/icons/retry.d.ts +2 -0
- package/lib/types/icons/retry.d.ts.map +1 -0
- package/lib/types/icons/send-prompt.d.ts +2 -0
- package/lib/types/icons/send-prompt.d.ts.map +1 -0
- package/lib/types/icons/send.d.ts +2 -0
- package/lib/types/icons/send.d.ts.map +1 -0
- package/lib/types/icons/shorter.d.ts +2 -0
- package/lib/types/icons/shorter.d.ts.map +1 -0
- package/lib/types/icons/translate.d.ts +2 -0
- package/lib/types/icons/translate.d.ts.map +1 -0
- package/lib/types/llm-providers/anthropic/index.d.ts +21 -0
- package/lib/types/llm-providers/anthropic/index.d.ts.map +1 -0
- package/lib/types/llm-providers/openai/index.d.ts +15 -0
- package/lib/types/llm-providers/openai/index.d.ts.map +1 -0
- package/lib/types/llm-providers/providers.spec.d.ts +2 -0
- package/lib/types/llm-providers/providers.spec.d.ts.map +1 -0
- package/lib/types/llm-providers/shared.d.ts +16 -0
- package/lib/types/llm-providers/shared.d.ts.map +1 -0
- package/lib/types/utils/group-builder.d.ts +1 -1
- package/lib/types/utils/group-builder.d.ts.map +1 -1
- package/lib/types/utils/keep-alive.d.ts +2 -0
- package/lib/types/utils/keep-alive.d.ts.map +1 -0
- package/package.json +34 -13
- package/src/core/builder.ts +39 -2
- package/src/feature/ai/ai.spec.ts +742 -0
- package/src/feature/ai/commands.ts +257 -0
- package/src/feature/ai/context.ts +45 -0
- package/src/feature/ai/diff-actions/index.ts +95 -0
- package/src/feature/ai/diff-actions/view.ts +237 -0
- package/src/feature/ai/index.ts +118 -0
- package/src/feature/ai/instruction-tooltip/component.tsx +414 -0
- package/src/feature/ai/instruction-tooltip/index.ts +101 -0
- package/src/feature/ai/instruction-tooltip/suggestions.ts +249 -0
- package/src/feature/ai/instruction-tooltip/view.ts +159 -0
- package/src/feature/ai/streaming-indicator.ts +183 -0
- package/src/feature/ai/types.ts +178 -0
- package/src/feature/block-edit/handle/component.tsx +3 -2
- package/src/feature/block-edit/menu/component.tsx +3 -2
- package/src/feature/block-edit/menu/config.ts +1 -1
- package/src/feature/image-block/index.ts +4 -0
- package/src/feature/index.ts +14 -2
- package/src/feature/latex/inline-tooltip/component.tsx +4 -2
- package/src/feature/latex/inline-tooltip/inline-tooltip.spec.ts +81 -0
- package/src/feature/latex/inline-tooltip/view.ts +2 -0
- package/src/feature/loader.ts +8 -0
- package/src/feature/toolbar/component.tsx +7 -5
- package/src/feature/toolbar/config.ts +27 -1
- package/src/feature/toolbar/index.ts +1 -0
- package/src/feature/top-bar/component.tsx +198 -0
- package/src/feature/top-bar/config.ts +367 -0
- package/src/feature/top-bar/index.ts +113 -0
- package/src/icons/ai.ts +14 -0
- package/src/icons/chevron-left.ts +15 -0
- package/src/icons/chevron-right.ts +15 -0
- package/src/icons/code-block.ts +12 -0
- package/src/icons/enter-key.ts +13 -0
- package/src/icons/grammar-check.ts +13 -0
- package/src/icons/index.ts +12 -0
- package/src/icons/longer.ts +13 -0
- package/src/icons/retry.ts +13 -0
- package/src/icons/send-prompt.ts +13 -0
- package/src/icons/send.ts +13 -0
- package/src/icons/shorter.ts +13 -0
- package/src/icons/translate.ts +13 -0
- package/src/llm-providers/anthropic/index.ts +132 -0
- package/src/llm-providers/openai/index.ts +109 -0
- package/src/llm-providers/providers.spec.ts +472 -0
- package/src/llm-providers/shared.ts +160 -0
- package/src/theme/common/ai.css +430 -0
- package/src/theme/common/code-mirror.css +14 -0
- package/src/theme/common/diff.css +196 -0
- package/src/theme/common/style.css +3 -0
- package/src/theme/common/top-bar.css +156 -0
- package/src/utils/group-builder.ts +1 -1
- package/src/utils/keep-alive.ts +3 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import type { Ctx } from '@milkdown/kit/ctx'
|
|
2
|
+
import type { MilkdownError } from '@milkdown/kit/exception'
|
|
3
|
+
import type { StreamingConfig } from '@milkdown/kit/plugin/streaming'
|
|
4
|
+
|
|
5
|
+
import type { AISuggestionsBuilder } from './instruction-tooltip/suggestions'
|
|
6
|
+
|
|
7
|
+
export type {
|
|
8
|
+
AISubmenuBuilder,
|
|
9
|
+
AISubmenuDef,
|
|
10
|
+
AISuggestionItem,
|
|
11
|
+
AISuggestionsBuilder,
|
|
12
|
+
} from './instruction-tooltip/suggestions'
|
|
13
|
+
|
|
14
|
+
export interface AIPromptContext {
|
|
15
|
+
document: string
|
|
16
|
+
selection: string
|
|
17
|
+
instruction: string
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/// An async generator that yields markdown tokens from an LLM or
|
|
21
|
+
/// similar source. Called by `runAICmd` with the assembled prompt
|
|
22
|
+
/// context and an abort signal.
|
|
23
|
+
export type AIProvider = (
|
|
24
|
+
context: AIPromptContext,
|
|
25
|
+
signal: AbortSignal
|
|
26
|
+
) => AsyncIterable<string>
|
|
27
|
+
|
|
28
|
+
/// Diff-related config options passed through to the diff plugin and component.
|
|
29
|
+
export interface AIDiffConfig {
|
|
30
|
+
acceptLabel?: string
|
|
31
|
+
rejectLabel?: string
|
|
32
|
+
customBlockTypes?: string[]
|
|
33
|
+
ignoreAttrs?: Record<string, string[]>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/// Customize the inline streaming indicator pill shown while AI runs.
|
|
37
|
+
export interface AIStreamingIndicatorConfig {
|
|
38
|
+
/// Fallback active-form label when the current session has none set
|
|
39
|
+
/// (i.e., `runAICmd` was called without a `label`).
|
|
40
|
+
/// @default DEFAULT_STREAMING_FALLBACK_LABEL
|
|
41
|
+
fallbackLabel?: string
|
|
42
|
+
|
|
43
|
+
/// Hint text shown after the label, suggesting how to cancel.
|
|
44
|
+
/// @default DEFAULT_STREAMING_CANCEL_HINT
|
|
45
|
+
cancelHint?: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/// Customize the floating diff actions panel (Retry / Reject all / Accept all).
|
|
49
|
+
///
|
|
50
|
+
/// Note: the enter-key icon used in the Accept-all shortcut chip is
|
|
51
|
+
/// shared with the instruction tooltip and overridden via the top-level
|
|
52
|
+
/// `AIFeatureConfig.enterKeyIcon`.
|
|
53
|
+
export interface AIDiffActionsConfig {
|
|
54
|
+
/// @default DEFAULT_DIFF_ACTIONS_RETRY_LABEL
|
|
55
|
+
retryLabel?: string
|
|
56
|
+
|
|
57
|
+
/// @default DEFAULT_DIFF_ACTIONS_REJECT_ALL_LABEL
|
|
58
|
+
rejectAllLabel?: string
|
|
59
|
+
|
|
60
|
+
/// @default DEFAULT_DIFF_ACTIONS_ACCEPT_ALL_LABEL
|
|
61
|
+
acceptAllLabel?: string
|
|
62
|
+
|
|
63
|
+
/// Icon for the Retry button. Default: refresh icon.
|
|
64
|
+
retryIcon?: string
|
|
65
|
+
|
|
66
|
+
/// Icon for the Reject all button. Default: 'X' icon.
|
|
67
|
+
rejectIcon?: string
|
|
68
|
+
|
|
69
|
+
/// Icon for the Accept all button. Default: checkmark icon.
|
|
70
|
+
acceptIcon?: string
|
|
71
|
+
|
|
72
|
+
/// Modifier key glyph shown alongside the enter-key icon. Set to
|
|
73
|
+
/// 'Ctrl' on non-Mac platforms if you need to disambiguate.
|
|
74
|
+
/// @default DEFAULT_DIFF_ACTIONS_MOD_SYMBOL
|
|
75
|
+
modSymbol?: string
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/// Configuration for `CrepeFeature.AI`.
|
|
79
|
+
export interface AIFeatureConfig {
|
|
80
|
+
/// Async generator that yields markdown tokens. Required for
|
|
81
|
+
/// `runAICmd`; without it the command returns false. The diff and
|
|
82
|
+
/// streaming plugins load either way, so the feature can be enabled
|
|
83
|
+
/// without a provider for diff-only or manual-streaming use cases.
|
|
84
|
+
provider?: AIProvider
|
|
85
|
+
|
|
86
|
+
/// Optional. Assemble the context passed to `provider`.
|
|
87
|
+
/// Defaults to serializing the document (+ selection if any) as markdown.
|
|
88
|
+
buildContext?: (ctx: Ctx, instruction: string) => AIPromptContext
|
|
89
|
+
|
|
90
|
+
/// Whether to enter diff review after streaming completes. Default true.
|
|
91
|
+
diffReviewOnEnd?: boolean
|
|
92
|
+
|
|
93
|
+
/// Pass-through config for the diff plugin.
|
|
94
|
+
diff?: AIDiffConfig
|
|
95
|
+
|
|
96
|
+
/// Pass-through config for the streaming plugin.
|
|
97
|
+
/// `diffReviewOnEnd` is excluded here because it's controlled by
|
|
98
|
+
/// `AIFeatureConfig.diffReviewOnEnd` at the AI layer — setting it on
|
|
99
|
+
/// both would be confusing.
|
|
100
|
+
streaming?: Partial<Omit<StreamingConfig, 'diffReviewOnEnd'>>
|
|
101
|
+
|
|
102
|
+
/// Called when an error occurs during AI processing.
|
|
103
|
+
/// Receives a `MilkdownError` with code `aiProviderError` or
|
|
104
|
+
/// `aiBuildContextError`.
|
|
105
|
+
onError?: (error: MilkdownError) => void
|
|
106
|
+
|
|
107
|
+
/// Custom icon for both the toolbar AI entry point and the prefix slot
|
|
108
|
+
/// inside the instruction input. The toolbar feature can override this
|
|
109
|
+
/// for just the toolbar button via `ToolbarFeatureConfig.aiIcon`.
|
|
110
|
+
aiIcon?: string
|
|
111
|
+
|
|
112
|
+
/// Placeholder text for the AI instruction input on the main view.
|
|
113
|
+
/// @default DEFAULT_INSTRUCTION_PLACEHOLDER
|
|
114
|
+
instructionPlaceholder?: string
|
|
115
|
+
|
|
116
|
+
/// Label for the suggestions section header.
|
|
117
|
+
/// @default DEFAULT_SUGGESTIONS_HEADER_LABEL
|
|
118
|
+
suggestionsHeaderLabel?: string
|
|
119
|
+
|
|
120
|
+
/// Label for the free-text prompt section header.
|
|
121
|
+
/// @default DEFAULT_SEND_AS_PROMPT_HEADER_LABEL
|
|
122
|
+
sendAsPromptHeaderLabel?: string
|
|
123
|
+
|
|
124
|
+
/// Prefix text for the free-text prompt entry, before the quoted input.
|
|
125
|
+
/// @default DEFAULT_SEND_AS_PROMPT_LABEL
|
|
126
|
+
sendAsPromptLabel?: string
|
|
127
|
+
|
|
128
|
+
/// Accessible name for the round submit button in the input pill.
|
|
129
|
+
/// Surfaced as `aria-label` so screen readers don't announce the
|
|
130
|
+
/// icon-only button as "unlabeled".
|
|
131
|
+
/// @default DEFAULT_SUBMIT_BUTTON_LABEL
|
|
132
|
+
submitButtonLabel?: string
|
|
133
|
+
|
|
134
|
+
/// Accessible name announced for the suggestion list (`role="listbox"`)
|
|
135
|
+
/// inside the palette. Localize alongside the other strings.
|
|
136
|
+
/// @default DEFAULT_LISTBOX_LABEL
|
|
137
|
+
listboxLabel?: string
|
|
138
|
+
|
|
139
|
+
/// Icon for the round submit button in the input pill.
|
|
140
|
+
/// Default: an upward arrow.
|
|
141
|
+
sendIcon?: string
|
|
142
|
+
|
|
143
|
+
/// Icon shown next to the "Ask AI: …" entry. Default: paper-plane.
|
|
144
|
+
sendPromptIcon?: string
|
|
145
|
+
|
|
146
|
+
/// Icon used in the keyboard shortcut chip on the prompt entry.
|
|
147
|
+
/// Default: enter-key arrow.
|
|
148
|
+
enterKeyIcon?: string
|
|
149
|
+
|
|
150
|
+
/// Icon for the back arrow at the top of a submenu. Default: chevron-left.
|
|
151
|
+
chevronLeftIcon?: string
|
|
152
|
+
|
|
153
|
+
/// Icon shown on the right of submenu entries. Default: chevron-right.
|
|
154
|
+
chevronRightIcon?: string
|
|
155
|
+
|
|
156
|
+
/// Customize the suggestion list. The builder is pre-populated with the
|
|
157
|
+
/// built-in suggestions; the callback can add, remove, or replace any
|
|
158
|
+
/// item or submenu. To start from scratch, call `builder.clear()` first.
|
|
159
|
+
buildAISuggestions?: (builder: AISuggestionsBuilder) => void
|
|
160
|
+
|
|
161
|
+
/// Customize the inline streaming indicator pill shown while AI runs.
|
|
162
|
+
streamingIndicator?: AIStreamingIndicatorConfig
|
|
163
|
+
|
|
164
|
+
/// Customize the floating diff actions panel (Retry / Reject all /
|
|
165
|
+
/// Accept all) that appears once streaming hands off to diff review.
|
|
166
|
+
diffActions?: AIDiffActionsConfig
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/// Options passed to `runAICmd`.
|
|
170
|
+
export interface RunAIOptions {
|
|
171
|
+
/// The user instruction to send to the AI provider.
|
|
172
|
+
instruction: string
|
|
173
|
+
|
|
174
|
+
/// Optional active-form label shown in the streaming indicator
|
|
175
|
+
/// (e.g., "Improving writing", "Translating"). When omitted, falls
|
|
176
|
+
/// back to `streamingIndicator.fallbackLabel` (default `'Generating'`).
|
|
177
|
+
label?: string
|
|
178
|
+
}
|
|
@@ -14,9 +14,10 @@ import {
|
|
|
14
14
|
|
|
15
15
|
import type { BlockEditFeatureConfig } from '..'
|
|
16
16
|
|
|
17
|
+
import { keepAlive } from '../../../utils/keep-alive'
|
|
17
18
|
import { getGroups } from './config'
|
|
18
19
|
|
|
19
|
-
h
|
|
20
|
+
keepAlive(h)
|
|
20
21
|
|
|
21
22
|
type MenuProps = {
|
|
22
23
|
ctx: Ctx
|
|
@@ -91,7 +92,7 @@ export const Menu = defineComponent<MenuProps>({
|
|
|
91
92
|
const item = groupInfo.value.groups
|
|
92
93
|
.flatMap((group) => group.items)
|
|
93
94
|
.at(index)
|
|
94
|
-
if (item && ctx) item.onRun(ctx)
|
|
95
|
+
if (item?.onRun && ctx) item.onRun(ctx)
|
|
95
96
|
|
|
96
97
|
hide()
|
|
97
98
|
}
|
|
@@ -31,6 +31,8 @@ interface ImageBlockConfig {
|
|
|
31
31
|
blockUploadPlaceholderText: string
|
|
32
32
|
blockOnUpload: (file: File) => Promise<string>
|
|
33
33
|
onImageLoadError: (event: Event) => void | Promise<void>
|
|
34
|
+
maxWidth: number
|
|
35
|
+
maxHeight: number
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
export type ImageBlockFeatureConfig = Partial<ImageBlockConfig>
|
|
@@ -63,6 +65,8 @@ export const imageBlock: DefineFeature<ImageBlockFeatureConfig> = (
|
|
|
63
65
|
onUpload: config?.blockOnUpload ?? config?.onUpload ?? value.onUpload,
|
|
64
66
|
proxyDomURL: config?.proxyDomURL,
|
|
65
67
|
onImageLoadError: config?.onImageLoadError ?? value.onImageLoadError,
|
|
68
|
+
maxWidth: config?.maxWidth,
|
|
69
|
+
maxHeight: config?.maxHeight,
|
|
66
70
|
}))
|
|
67
71
|
})
|
|
68
72
|
.use(imageBlockComponent)
|
package/src/feature/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AIFeatureConfig } from './ai'
|
|
1
2
|
import type { BlockEditFeatureConfig } from './block-edit'
|
|
2
3
|
import type { CodeMirrorFeatureConfig } from './code-mirror'
|
|
3
4
|
import type { CursorFeatureConfig } from './cursor'
|
|
@@ -8,10 +9,11 @@ import type { ListItemFeatureConfig } from './list-item'
|
|
|
8
9
|
import type { PlaceholderFeatureConfig } from './placeholder'
|
|
9
10
|
import type { TableFeatureConfig } from './table'
|
|
10
11
|
import type { ToolbarFeatureConfig } from './toolbar'
|
|
12
|
+
import type { TopBarFeatureConfig } from './top-bar'
|
|
11
13
|
|
|
12
14
|
/// The crepe editor feature flags.
|
|
13
|
-
///
|
|
14
|
-
///
|
|
15
|
+
/// Most features are enabled by default; `TopBar` and `AI` are opt-in.
|
|
16
|
+
/// See `defaultFeatures` for the per-flag default.
|
|
15
17
|
export enum CrepeFeature {
|
|
16
18
|
/// Syntax highlighting and editing for code blocks with language support, theme customization, and preview capabilities.
|
|
17
19
|
CodeMirror = 'code-mirror',
|
|
@@ -42,6 +44,12 @@ export enum CrepeFeature {
|
|
|
42
44
|
|
|
43
45
|
/// Mathematical formula support with both inline and block math rendering using KaTeX.
|
|
44
46
|
Latex = 'latex',
|
|
47
|
+
|
|
48
|
+
/// Fixed top toolbar with heading selector, formatting buttons, insert actions, and block commands.
|
|
49
|
+
TopBar = 'top-bar',
|
|
50
|
+
|
|
51
|
+
/// AI-assisted editing: streaming input, diff review, and provider integration.
|
|
52
|
+
AI = 'ai',
|
|
45
53
|
}
|
|
46
54
|
|
|
47
55
|
export interface CrepeFeatureConfig {
|
|
@@ -55,6 +63,8 @@ export interface CrepeFeatureConfig {
|
|
|
55
63
|
[CrepeFeature.CodeMirror]?: CodeMirrorFeatureConfig
|
|
56
64
|
[CrepeFeature.Table]?: TableFeatureConfig
|
|
57
65
|
[CrepeFeature.Latex]?: LatexFeatureConfig
|
|
66
|
+
[CrepeFeature.TopBar]?: TopBarFeatureConfig
|
|
67
|
+
[CrepeFeature.AI]?: AIFeatureConfig
|
|
58
68
|
}
|
|
59
69
|
|
|
60
70
|
export const defaultFeatures: Record<CrepeFeature, boolean> = {
|
|
@@ -68,4 +78,6 @@ export const defaultFeatures: Record<CrepeFeature, boolean> = {
|
|
|
68
78
|
[CrepeFeature.CodeMirror]: true,
|
|
69
79
|
[CrepeFeature.Table]: true,
|
|
70
80
|
[CrepeFeature.Latex]: true,
|
|
81
|
+
[CrepeFeature.TopBar]: false,
|
|
82
|
+
[CrepeFeature.AI]: false,
|
|
71
83
|
}
|
|
@@ -5,14 +5,16 @@ import { defineComponent, type ShallowRef, type VNodeRef, h } from 'vue'
|
|
|
5
5
|
|
|
6
6
|
import type { LatexConfig } from '..'
|
|
7
7
|
|
|
8
|
+
import { keepAlive } from '../../../utils/keep-alive'
|
|
9
|
+
|
|
10
|
+
keepAlive(h)
|
|
11
|
+
|
|
8
12
|
type LatexTooltipProps = {
|
|
9
13
|
config: Partial<LatexConfig>
|
|
10
14
|
innerView: ShallowRef<EditorView | null>
|
|
11
15
|
updateValue: ShallowRef<() => void>
|
|
12
16
|
}
|
|
13
17
|
|
|
14
|
-
h
|
|
15
|
-
|
|
16
18
|
export const LatexTooltip = defineComponent<LatexTooltipProps>({
|
|
17
19
|
props: {
|
|
18
20
|
config: {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import '@testing-library/jest-dom/vitest'
|
|
2
|
+
import type { Node } from '@milkdown/kit/prose/model'
|
|
3
|
+
|
|
4
|
+
import { editorViewCtx } from '@milkdown/kit/core'
|
|
5
|
+
import { NodeSelection } from '@milkdown/kit/prose/state'
|
|
6
|
+
import { afterEach, describe, expect, test } from 'vitest'
|
|
7
|
+
|
|
8
|
+
import { Crepe } from '../../../core'
|
|
9
|
+
import { mathInlineId } from '../inline-latex'
|
|
10
|
+
|
|
11
|
+
function waitForAsync() {
|
|
12
|
+
return new Promise<void>((resolve) => setTimeout(resolve, 0))
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function findMathInlinePos(doc: Node) {
|
|
16
|
+
let pos: number | undefined
|
|
17
|
+
doc.descendants((node, currentPos) => {
|
|
18
|
+
if (node.type.name === mathInlineId) {
|
|
19
|
+
pos = currentPos
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
return true
|
|
23
|
+
})
|
|
24
|
+
return pos
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
describe('latex inline tooltip', () => {
|
|
28
|
+
afterEach(() => {
|
|
29
|
+
document.body.replaceChildren()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('should show in edit mode', async () => {
|
|
33
|
+
const crepe = new Crepe({
|
|
34
|
+
defaultValue: '$x$',
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
await crepe.create()
|
|
38
|
+
|
|
39
|
+
const view = crepe.editor.ctx.get(editorViewCtx)
|
|
40
|
+
const pos = findMathInlinePos(view.state.doc)
|
|
41
|
+
expect(pos).toBeTypeOf('number')
|
|
42
|
+
|
|
43
|
+
view.dispatch(
|
|
44
|
+
view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos!))
|
|
45
|
+
)
|
|
46
|
+
await waitForAsync()
|
|
47
|
+
|
|
48
|
+
expect(view.state.selection).toBeInstanceOf(NodeSelection)
|
|
49
|
+
expect((view.state.selection as NodeSelection).node.type.name).toBe(
|
|
50
|
+
mathInlineId
|
|
51
|
+
)
|
|
52
|
+
expect(
|
|
53
|
+
document.body.querySelector('.milkdown-latex-inline-edit')
|
|
54
|
+
).toHaveAttribute('data-show', 'true')
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('should not show in readonly editor', async () => {
|
|
58
|
+
const crepe = new Crepe({
|
|
59
|
+
defaultValue: '$x$',
|
|
60
|
+
}).setReadonly(true)
|
|
61
|
+
|
|
62
|
+
await crepe.create()
|
|
63
|
+
|
|
64
|
+
const view = crepe.editor.ctx.get(editorViewCtx)
|
|
65
|
+
const pos = findMathInlinePos(view.state.doc)
|
|
66
|
+
expect(pos).toBeTypeOf('number')
|
|
67
|
+
|
|
68
|
+
view.dispatch(
|
|
69
|
+
view.state.tr.setSelection(NodeSelection.create(view.state.doc, pos!))
|
|
70
|
+
)
|
|
71
|
+
await waitForAsync()
|
|
72
|
+
|
|
73
|
+
expect(view.state.selection).toBeInstanceOf(NodeSelection)
|
|
74
|
+
expect((view.state.selection as NodeSelection).node.type.name).toBe(
|
|
75
|
+
mathInlineId
|
|
76
|
+
)
|
|
77
|
+
expect(
|
|
78
|
+
document.body.querySelector('.milkdown-latex-inline-edit')
|
|
79
|
+
).toHaveAttribute('data-show', 'false')
|
|
80
|
+
})
|
|
81
|
+
})
|
|
@@ -58,6 +58,8 @@ export class LatexInlineTooltip implements PluginView {
|
|
|
58
58
|
|
|
59
59
|
#shouldShow = (view: EditorView) => {
|
|
60
60
|
const shouldShow = () => {
|
|
61
|
+
if (!view.editable) return false
|
|
62
|
+
|
|
61
63
|
const { selection, schema } = view.state
|
|
62
64
|
if (selection.empty) return false
|
|
63
65
|
if (!(selection instanceof NodeSelection)) return false
|
package/src/feature/loader.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Editor } from '@milkdown/kit/core'
|
|
2
2
|
|
|
3
|
+
import { ai } from './ai'
|
|
3
4
|
import { blockEdit } from './block-edit'
|
|
4
5
|
import { codeMirror } from './code-mirror'
|
|
5
6
|
import { cursor } from './cursor'
|
|
@@ -11,6 +12,7 @@ import { listItem } from './list-item'
|
|
|
11
12
|
import { placeholder } from './placeholder'
|
|
12
13
|
import { table } from './table'
|
|
13
14
|
import { toolbar } from './toolbar'
|
|
15
|
+
import { topBar } from './top-bar'
|
|
14
16
|
|
|
15
17
|
export function loadFeature(
|
|
16
18
|
feature: CrepeFeature,
|
|
@@ -48,5 +50,11 @@ export function loadFeature(
|
|
|
48
50
|
case CrepeFeature.Latex: {
|
|
49
51
|
return latex(editor, config)
|
|
50
52
|
}
|
|
53
|
+
case CrepeFeature.TopBar: {
|
|
54
|
+
return topBar(editor, config)
|
|
55
|
+
}
|
|
56
|
+
case CrepeFeature.AI: {
|
|
57
|
+
return ai(editor, config)
|
|
58
|
+
}
|
|
51
59
|
}
|
|
52
60
|
}
|
|
@@ -15,10 +15,10 @@ import {
|
|
|
15
15
|
|
|
16
16
|
import type { ToolbarFeatureConfig } from '.'
|
|
17
17
|
|
|
18
|
+
import { keepAlive } from '../../utils/keep-alive'
|
|
18
19
|
import { getGroups, type ToolbarItem } from './config'
|
|
19
20
|
|
|
20
|
-
h
|
|
21
|
-
Fragment
|
|
21
|
+
keepAlive(h, Fragment)
|
|
22
22
|
|
|
23
23
|
type ToolbarProps = {
|
|
24
24
|
ctx: Ctx
|
|
@@ -54,14 +54,16 @@ export const Toolbar = defineComponent<ToolbarProps>({
|
|
|
54
54
|
setup(props) {
|
|
55
55
|
const { ctx, config } = props
|
|
56
56
|
|
|
57
|
-
const onClick = (fn
|
|
57
|
+
const onClick = (fn?: (ctx: Ctx) => void) => (e: MouseEvent) => {
|
|
58
58
|
e.preventDefault()
|
|
59
|
-
|
|
59
|
+
if (ctx) {
|
|
60
|
+
fn?.(ctx)
|
|
61
|
+
}
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
function checkActive(checker: ToolbarItem['active']) {
|
|
63
65
|
// make sure the function subscribed to vue reactive
|
|
64
|
-
props.selection.value
|
|
66
|
+
keepAlive(props.selection.value)
|
|
65
67
|
// Check if the edtior is ready
|
|
66
68
|
const status = ctx.get(editorCtx).status
|
|
67
69
|
if (status !== EditorStatus.Created) return false
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Ctx } from '@milkdown/kit/ctx'
|
|
2
2
|
|
|
3
3
|
import { toggleLinkCommand } from '@milkdown/kit/component/link-tooltip'
|
|
4
|
-
import { commandsCtx } from '@milkdown/kit/core'
|
|
4
|
+
import { commandsCtx, editorViewCtx } from '@milkdown/kit/core'
|
|
5
5
|
import {
|
|
6
6
|
emphasisSchema,
|
|
7
7
|
inlineCodeSchema,
|
|
@@ -23,6 +23,7 @@ import type { ToolbarFeatureConfig } from '.'
|
|
|
23
23
|
import { CrepeFeature } from '..'
|
|
24
24
|
import { useCrepeFeatures } from '../../core/slice'
|
|
25
25
|
import {
|
|
26
|
+
aiIcon,
|
|
26
27
|
boldIcon,
|
|
27
28
|
codeIcon,
|
|
28
29
|
functionsIcon,
|
|
@@ -31,6 +32,8 @@ import {
|
|
|
31
32
|
strikethroughIcon,
|
|
32
33
|
} from '../../icons'
|
|
33
34
|
import { GroupBuilder } from '../../utils/group-builder'
|
|
35
|
+
import { aiProviderConfig } from '../ai/commands'
|
|
36
|
+
import { aiInstructionTooltipAPI } from '../ai/instruction-tooltip'
|
|
34
37
|
import { toggleLatexCommand } from '../latex/command'
|
|
35
38
|
import { mathInlineSchema } from '../latex/inline-latex'
|
|
36
39
|
|
|
@@ -130,6 +133,29 @@ export function getGroups(config?: ToolbarFeatureConfig, ctx?: Ctx) {
|
|
|
130
133
|
},
|
|
131
134
|
})
|
|
132
135
|
|
|
136
|
+
// Skip the AI button entirely when the feature is disabled or when no
|
|
137
|
+
// provider is configured — without a provider the palette would open
|
|
138
|
+
// but `runAICmd` would silently reject every action. Toolbar-level
|
|
139
|
+
// `aiIcon` wins over `AIFeatureConfig.aiIcon` so consumers can override
|
|
140
|
+
// just the toolbar entry without touching the tooltip prefix.
|
|
141
|
+
// The aiProviderConfig slice is only injected when the AI feature is
|
|
142
|
+
// active, so guard the lookup behind the flag check.
|
|
143
|
+
if (ctx && flags?.includes(CrepeFeature.AI)) {
|
|
144
|
+
const aiCfg = ctx.get(aiProviderConfig.key)
|
|
145
|
+
if (aiCfg.provider) {
|
|
146
|
+
functionGroup.addItem('ai', {
|
|
147
|
+
icon: config?.aiIcon ?? aiCfg.aiIcon ?? aiIcon,
|
|
148
|
+
active: () => false,
|
|
149
|
+
onRun: (ctx) => {
|
|
150
|
+
const api = ctx.get(aiInstructionTooltipAPI.key)
|
|
151
|
+
const view = ctx.get(editorViewCtx)
|
|
152
|
+
const { from, to } = view.state.selection
|
|
153
|
+
api.show(from, to)
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
133
159
|
config?.buildToolbar?.(groupBuilder)
|
|
134
160
|
|
|
135
161
|
return groupBuilder.build()
|