@walkinissue/angy 0.2.17
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/LICENSE +21 -0
- package/README.md +246 -0
- package/dist/client/Angy.svelte +787 -0
- package/dist/client/PendingChangesDialog.svelte +109 -0
- package/dist/client/TranslationAlternativeItem.svelte +309 -0
- package/dist/client/TranslationHelperForm.svelte +645 -0
- package/dist/client/VibeTooltip.svelte +146 -0
- package/dist/client/dragItem.ts +50 -0
- package/dist/client/toggleQA.shared.ts +164 -0
- package/dist/client/translationSuggestions.ts +100 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/plugin.d.ts +26 -0
- package/dist/plugin.js +288 -0
- package/dist/server/types.ts +40 -0
- package/dist/server.d.ts +95 -0
- package/dist/server.js +1094 -0
- package/dist/server.js.map +7 -0
- package/package.json +85 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
let {
|
|
3
|
+
text,
|
|
4
|
+
position = 'top',
|
|
5
|
+
disabled = false,
|
|
6
|
+
delay = 120
|
|
7
|
+
}: {
|
|
8
|
+
text: string;
|
|
9
|
+
position?: 'top' | 'bottom' | 'left' | 'right';
|
|
10
|
+
disabled?: boolean;
|
|
11
|
+
delay?: number;
|
|
12
|
+
} = $props();
|
|
13
|
+
|
|
14
|
+
let visible = $state(false);
|
|
15
|
+
let timeout: ReturnType<typeof setTimeout> | null = null;
|
|
16
|
+
const id = `tooltip-${Math.random().toString(36).slice(2, 10)}`;
|
|
17
|
+
|
|
18
|
+
function show() {
|
|
19
|
+
if (disabled || !text) return;
|
|
20
|
+
clearDelay();
|
|
21
|
+
timeout = setTimeout(() => {
|
|
22
|
+
visible = true;
|
|
23
|
+
}, delay);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function hide() {
|
|
27
|
+
clearDelay();
|
|
28
|
+
visible = false;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function clearDelay() {
|
|
32
|
+
if (timeout) {
|
|
33
|
+
clearTimeout(timeout);
|
|
34
|
+
timeout = null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<div
|
|
40
|
+
class="tooltip-wrap"
|
|
41
|
+
onmouseenter={show}
|
|
42
|
+
onmouseleave={hide}
|
|
43
|
+
onfocusin={show}
|
|
44
|
+
onfocusout={hide}
|
|
45
|
+
>
|
|
46
|
+
<slot />
|
|
47
|
+
|
|
48
|
+
{#if visible && !disabled && text}
|
|
49
|
+
<div
|
|
50
|
+
id={id}
|
|
51
|
+
role="tooltip"
|
|
52
|
+
class={`tooltip tooltip--${position}`}
|
|
53
|
+
>
|
|
54
|
+
{text}
|
|
55
|
+
</div>
|
|
56
|
+
{/if}
|
|
57
|
+
</div>
|
|
58
|
+
|
|
59
|
+
<style>
|
|
60
|
+
.tooltip-wrap {
|
|
61
|
+
position: relative;
|
|
62
|
+
display: inline-flex;
|
|
63
|
+
width: fit-content;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.tooltip {
|
|
67
|
+
position: absolute;
|
|
68
|
+
z-index: 1000;
|
|
69
|
+
/*max-width: 18rem; /*:S*/
|
|
70
|
+
padding: 0.45rem 0.65rem;
|
|
71
|
+
border-radius: 0.5rem;
|
|
72
|
+
background: rgba(20, 20, 20, 0.96);
|
|
73
|
+
color: white;
|
|
74
|
+
font-size: 0.8rem;
|
|
75
|
+
line-height: 1.2;
|
|
76
|
+
white-space: nowrap;
|
|
77
|
+
pointer-events: none;
|
|
78
|
+
box-shadow:
|
|
79
|
+
0 8px 24px rgba(0, 0, 0, 0.22),
|
|
80
|
+
0 2px 8px rgba(0, 0, 0, 0.14);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
.tooltip::after {
|
|
84
|
+
content: '';
|
|
85
|
+
position: absolute;
|
|
86
|
+
width: 0;
|
|
87
|
+
height: 0;
|
|
88
|
+
border-style: solid;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
.tooltip--top {
|
|
92
|
+
bottom: calc(100% + 0.5rem);
|
|
93
|
+
left: 50%;
|
|
94
|
+
transform: translateX(-50%);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
.tooltip--top::after {
|
|
98
|
+
top: 100%;
|
|
99
|
+
left: 50%;
|
|
100
|
+
transform: translateX(-50%);
|
|
101
|
+
border-width: 0.35rem 0.35rem 0 0.35rem;
|
|
102
|
+
border-color: rgba(20, 20, 20, 0.96) transparent transparent transparent;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.tooltip--bottom {
|
|
106
|
+
top: calc(100% + 0.5rem);
|
|
107
|
+
left: 50%;
|
|
108
|
+
transform: translateX(-50%);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
.tooltip--bottom::after {
|
|
112
|
+
bottom: 100%;
|
|
113
|
+
left: 50%;
|
|
114
|
+
transform: translateX(-50%);
|
|
115
|
+
border-width: 0 0.35rem 0.35rem 0.35rem;
|
|
116
|
+
border-color: transparent transparent rgba(20, 20, 20, 0.96) transparent;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.tooltip--left {
|
|
120
|
+
right: calc(100% + 0.5rem);
|
|
121
|
+
top: 50%;
|
|
122
|
+
transform: translateY(-50%);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
.tooltip--left::after {
|
|
126
|
+
left: 100%;
|
|
127
|
+
top: 50%;
|
|
128
|
+
transform: translateY(-50%);
|
|
129
|
+
border-width: 0.35rem 0 0.35rem 0.35rem;
|
|
130
|
+
border-color: transparent transparent transparent rgba(20, 20, 20, 0.96);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.tooltip--right {
|
|
134
|
+
left: calc(100% + 0.5rem);
|
|
135
|
+
top: 50%;
|
|
136
|
+
transform: translateY(-50%);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.tooltip--right::after {
|
|
140
|
+
right: 100%;
|
|
141
|
+
top: 50%;
|
|
142
|
+
transform: translateY(-50%);
|
|
143
|
+
border-width: 0.35rem 0.35rem 0.35rem 0;
|
|
144
|
+
border-color: transparent rgba(20, 20, 20, 0.96) transparent transparent;
|
|
145
|
+
}
|
|
146
|
+
</style>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
|
|
2
|
+
export function draggable(node: HTMLElement, param:{active: (activate:boolean) => void, drag: ()=> boolean}) {
|
|
3
|
+
|
|
4
|
+
let x = 100;
|
|
5
|
+
let y = 100;
|
|
6
|
+
let offsetX = 0;
|
|
7
|
+
let offsetY = 0;
|
|
8
|
+
let dragging = false;
|
|
9
|
+
|
|
10
|
+
function down(event: PointerEvent) {
|
|
11
|
+
dragging = param.drag();
|
|
12
|
+
offsetX = event.clientX - x;
|
|
13
|
+
offsetY = event.clientY - y;
|
|
14
|
+
|
|
15
|
+
window.addEventListener('pointermove', move);
|
|
16
|
+
window.addEventListener('pointerup', up);
|
|
17
|
+
param.active(false)
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function move(event: PointerEvent) {
|
|
21
|
+
if (!dragging) return;
|
|
22
|
+
x = event.clientX - offsetX;
|
|
23
|
+
y = event.clientY - offsetY;
|
|
24
|
+
node.style.left = `${x}px`;
|
|
25
|
+
node.style.top = `${y}px`;
|
|
26
|
+
param.active(true)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function up(event: Event) {
|
|
30
|
+
dragging = false;
|
|
31
|
+
window.removeEventListener('pointermove', move);
|
|
32
|
+
window.removeEventListener('pointerup', up);
|
|
33
|
+
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
node.style.position = 'fixed';
|
|
37
|
+
node.style.left = `${x}px`;
|
|
38
|
+
node.style.top = `${y}px`;
|
|
39
|
+
node.style.touchAction = 'none';
|
|
40
|
+
|
|
41
|
+
node.addEventListener('pointerdown', down);
|
|
42
|
+
|
|
43
|
+
return {
|
|
44
|
+
destroy() {
|
|
45
|
+
node.removeEventListener('pointerdown', down);
|
|
46
|
+
window.removeEventListener('pointermove', move);
|
|
47
|
+
window.removeEventListener('pointerup', up);
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
export type TranslationOrigin = "base" | "working";
|
|
2
|
+
|
|
3
|
+
export type ResolvedKey = {
|
|
4
|
+
msgid: string;
|
|
5
|
+
msgctxt: string | null;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
export type TranslationEntry = {
|
|
9
|
+
msgid: string;
|
|
10
|
+
msgctxt: string | null;
|
|
11
|
+
msgidPlural: string | null;
|
|
12
|
+
msgstr: string[];
|
|
13
|
+
references: string[];
|
|
14
|
+
extractedComments: string[];
|
|
15
|
+
flags: string[];
|
|
16
|
+
previous: string[];
|
|
17
|
+
obsolete: boolean;
|
|
18
|
+
hasTranslation?: boolean;
|
|
19
|
+
isFuzzy?: boolean | null;
|
|
20
|
+
isCommittedToWorking?: boolean;
|
|
21
|
+
matchesTargetTranslation?: boolean;
|
|
22
|
+
translationOrigin?: TranslationOrigin | null;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type TranslationAlternative = {
|
|
26
|
+
msgid: string;
|
|
27
|
+
msgctxt: string | null;
|
|
28
|
+
score: number;
|
|
29
|
+
references: string[];
|
|
30
|
+
msgstr: string[];
|
|
31
|
+
hasTranslation: boolean;
|
|
32
|
+
isFuzzy: boolean | null | undefined;
|
|
33
|
+
isCommittedToWorking?: boolean;
|
|
34
|
+
matchesTargetTranslation?: boolean;
|
|
35
|
+
translationOrigin?: TranslationOrigin | null;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export type TranslationContextResult = {
|
|
39
|
+
match: {
|
|
40
|
+
score: number;
|
|
41
|
+
via: string;
|
|
42
|
+
};
|
|
43
|
+
entry: TranslationEntry;
|
|
44
|
+
alternatives: TranslationAlternative[];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export type DraftTranslation = {
|
|
48
|
+
msgid: string;
|
|
49
|
+
msgctxt: string | null;
|
|
50
|
+
value: string;
|
|
51
|
+
origin: TranslationOrigin | null;
|
|
52
|
+
originalValue: string;
|
|
53
|
+
isDirty: boolean;
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
export type TranslationCandidate = Pick<
|
|
57
|
+
TranslationEntry,
|
|
58
|
+
"msgid" | "msgctxt" | "hasTranslation" | "translationOrigin" | "isFuzzy"
|
|
59
|
+
>;
|
|
60
|
+
|
|
61
|
+
export const TRANSLATION_STATUS_TOOLTIP =
|
|
62
|
+
"'\u{1F914}' fuzzy translation needs review. '\u2705' target and working agree. '\u274C' no translation. '\u{1F6E0}\uFE0F' working differs from target.";
|
|
63
|
+
|
|
64
|
+
export function translationKey(msgid: string, msgctxt: string | null) {
|
|
65
|
+
return `${msgctxt ?? ""}::${msgid}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function sameResolvedKey(
|
|
69
|
+
left: Pick<ResolvedKey, "msgid" | "msgctxt"> | null | undefined,
|
|
70
|
+
right: Pick<ResolvedKey, "msgid" | "msgctxt"> | null | undefined
|
|
71
|
+
) {
|
|
72
|
+
return (
|
|
73
|
+
Boolean(left && right) &&
|
|
74
|
+
left?.msgid === right?.msgid &&
|
|
75
|
+
(left?.msgctxt ?? null) === (right?.msgctxt ?? null)
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function getEffectiveResolvedKey(
|
|
80
|
+
selectedResolvedKey: ResolvedKey | null,
|
|
81
|
+
contextResult: TranslationContextResult | null
|
|
82
|
+
) {
|
|
83
|
+
return (
|
|
84
|
+
selectedResolvedKey ??
|
|
85
|
+
(contextResult?.entry
|
|
86
|
+
? {
|
|
87
|
+
msgid: contextResult.entry.msgid,
|
|
88
|
+
msgctxt: contextResult.entry.msgctxt
|
|
89
|
+
}
|
|
90
|
+
: null)
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function findContextEntry(
|
|
95
|
+
contextResult: TranslationContextResult | null,
|
|
96
|
+
msgid: string,
|
|
97
|
+
msgctxt: string | null
|
|
98
|
+
) {
|
|
99
|
+
if (!contextResult) return null;
|
|
100
|
+
|
|
101
|
+
if (
|
|
102
|
+
contextResult.entry.msgid === msgid &&
|
|
103
|
+
(contextResult.entry.msgctxt ?? null) === (msgctxt ?? null)
|
|
104
|
+
) {
|
|
105
|
+
return contextResult.entry;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return (
|
|
109
|
+
contextResult.alternatives.find(
|
|
110
|
+
(alternative) =>
|
|
111
|
+
alternative.msgid === msgid && (alternative.msgctxt ?? null) === (msgctxt ?? null)
|
|
112
|
+
) ?? null
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function getEntryOriginalValue(
|
|
117
|
+
contextResult: TranslationContextResult | null,
|
|
118
|
+
msgid: string,
|
|
119
|
+
msgctxt: string | null
|
|
120
|
+
) {
|
|
121
|
+
return findContextEntry(contextResult, msgid, msgctxt)?.msgstr?.[0] ?? "";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function getEntryOrigin(
|
|
125
|
+
contextResult: TranslationContextResult | null,
|
|
126
|
+
msgid: string,
|
|
127
|
+
msgctxt: string | null
|
|
128
|
+
): TranslationOrigin | null {
|
|
129
|
+
return findContextEntry(contextResult, msgid, msgctxt)?.translationOrigin ?? null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function getCandidateList(contextResult: TranslationContextResult | null): TranslationCandidate[] {
|
|
133
|
+
if (!contextResult) return [];
|
|
134
|
+
|
|
135
|
+
return [
|
|
136
|
+
{
|
|
137
|
+
msgid: contextResult.entry.msgid,
|
|
138
|
+
msgctxt: contextResult.entry.msgctxt,
|
|
139
|
+
translationOrigin: contextResult.entry.translationOrigin ?? null,
|
|
140
|
+
hasTranslation: contextResult.entry.hasTranslation ?? false,
|
|
141
|
+
isFuzzy: contextResult.entry.isFuzzy ?? null
|
|
142
|
+
},
|
|
143
|
+
...contextResult.alternatives.map((alternative) => ({
|
|
144
|
+
msgid: alternative.msgid,
|
|
145
|
+
msgctxt: alternative.msgctxt,
|
|
146
|
+
translationOrigin: alternative.translationOrigin ?? null,
|
|
147
|
+
hasTranslation: alternative.hasTranslation,
|
|
148
|
+
isFuzzy: alternative.isFuzzy ?? null
|
|
149
|
+
}))
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export function getTranslationStatus(item: {
|
|
154
|
+
isCommittedToWorking?: boolean;
|
|
155
|
+
matchesTargetTranslation?: boolean;
|
|
156
|
+
hasTranslation?: boolean;
|
|
157
|
+
isFuzzy?: boolean | null;
|
|
158
|
+
}) {
|
|
159
|
+
if (item.hasTranslation && item.matchesTargetTranslation) return "\u2705";
|
|
160
|
+
if (item.isCommittedToWorking) return "\u{1F6E0}\uFE0F";
|
|
161
|
+
if (!item.hasTranslation) return "\u274C";
|
|
162
|
+
if (item.isFuzzy) return "\u{1F914}";
|
|
163
|
+
return "\u2705";
|
|
164
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { translationKey, type TranslationContextResult } from "./toggleQA.shared";
|
|
2
|
+
|
|
3
|
+
const STORAGE_KEY = "i18n-translation-suggestions-v1";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
type SuggestionMap = Record<string, string>;
|
|
7
|
+
|
|
8
|
+
type SuggestionItem = {
|
|
9
|
+
msgid: string;
|
|
10
|
+
msgctxt: string | null;
|
|
11
|
+
suggestion: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function isBrowser() {
|
|
15
|
+
return typeof window !== "undefined" && typeof localStorage !== "undefined";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function readSuggestionCache(): SuggestionMap {
|
|
19
|
+
if (!isBrowser()) return {};
|
|
20
|
+
|
|
21
|
+
try {
|
|
22
|
+
const raw = localStorage.getItem(STORAGE_KEY);
|
|
23
|
+
if (!raw) return {};
|
|
24
|
+
const parsed = JSON.parse(raw);
|
|
25
|
+
return typeof parsed === "object" && parsed ? parsed : {};
|
|
26
|
+
} catch {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function writeSuggestionCache(cache: SuggestionMap) {
|
|
32
|
+
if (!isBrowser()) return;
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify(cache));
|
|
36
|
+
} catch {
|
|
37
|
+
// Ignore storage failures in this dev-only helper.
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function getEntriesForSuggestions(contextResult: TranslationContextResult) {
|
|
42
|
+
return [contextResult.entry, ...contextResult.alternatives];
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getUntranslatedItems(contextResult: TranslationContextResult, cache: SuggestionMap) {
|
|
46
|
+
return getEntriesForSuggestions(contextResult)
|
|
47
|
+
.filter((entry) => !entry.hasTranslation)
|
|
48
|
+
.filter((entry) => !cache[translationKey(entry.msgid, entry.msgctxt)])
|
|
49
|
+
.map((entry) => ({
|
|
50
|
+
msgid: entry.msgid,
|
|
51
|
+
msgctxt: entry.msgctxt
|
|
52
|
+
}));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function mergeSuggestionItems(cache: SuggestionMap, items: SuggestionItem[]) {
|
|
56
|
+
const nextCache = { ...cache };
|
|
57
|
+
for (const item of items) {
|
|
58
|
+
nextCache[translationKey(item.msgid, item.msgctxt)] = item.suggestion.trim();
|
|
59
|
+
}
|
|
60
|
+
return nextCache;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function requestTranslationSuggestions(
|
|
64
|
+
contextResult: TranslationContextResult,
|
|
65
|
+
endpoint: string
|
|
66
|
+
): Promise<SuggestionMap> {
|
|
67
|
+
const cache = readSuggestionCache();
|
|
68
|
+
const untranslatedItems = getUntranslatedItems(contextResult, cache);
|
|
69
|
+
if (!untranslatedItems.length) {
|
|
70
|
+
return cache;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const response = await fetch(`${endpoint}?intent=suggestions`, {
|
|
74
|
+
method: "POST",
|
|
75
|
+
headers: {
|
|
76
|
+
"content-type": "application/json"
|
|
77
|
+
},
|
|
78
|
+
body: JSON.stringify({
|
|
79
|
+
context: contextResult,
|
|
80
|
+
items: untranslatedItems
|
|
81
|
+
})
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (!response.ok) {
|
|
85
|
+
throw new Error(`Suggestion request failed: ${response.status}`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const json = await response.json();
|
|
89
|
+
if (json?.disabled) {
|
|
90
|
+
return cache;
|
|
91
|
+
}
|
|
92
|
+
const items = Array.isArray(json?.items) ? (json.items as SuggestionItem[]) : [];
|
|
93
|
+
if (!items.length) {
|
|
94
|
+
return cache;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const nextCache = mergeSuggestionItems(cache, items);
|
|
98
|
+
writeSuggestionCache(nextCache);
|
|
99
|
+
return nextCache;
|
|
100
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Angy } from "./client/Angy.svelte";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Angy } from "./client/Angy.svelte";
|
package/dist/plugin.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Plugin } from "vite";
|
|
2
|
+
import {
|
|
3
|
+
defineAngyConfig,
|
|
4
|
+
type AngyConfigInput,
|
|
5
|
+
type AngyResolvedConfig,
|
|
6
|
+
type SuggestionProvider,
|
|
7
|
+
type SuggestionProviderInput,
|
|
8
|
+
type SuggestionRequestItem,
|
|
9
|
+
type SuggestionResponseItem
|
|
10
|
+
} from "./server";
|
|
11
|
+
|
|
12
|
+
export type AngyPluginOptions = {
|
|
13
|
+
config?: Partial<AngyResolvedConfig>;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export declare function angy(options?: AngyPluginOptions): Plugin;
|
|
17
|
+
|
|
18
|
+
export {
|
|
19
|
+
defineAngyConfig,
|
|
20
|
+
type AngyConfigInput,
|
|
21
|
+
type AngyResolvedConfig,
|
|
22
|
+
type SuggestionProvider,
|
|
23
|
+
type SuggestionProviderInput,
|
|
24
|
+
type SuggestionRequestItem,
|
|
25
|
+
type SuggestionResponseItem
|
|
26
|
+
};
|