@ansiversa/components 0.0.165 → 0.0.167
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/package.json
CHANGED
|
@@ -1,54 +1,71 @@
|
|
|
1
1
|
---
|
|
2
|
+
type AssistMode = "suggestions" | "rewrite";
|
|
3
|
+
|
|
2
4
|
interface Props {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
mode?: AssistMode;
|
|
6
|
+
featureKey?: string;
|
|
7
|
+
value?: string;
|
|
8
|
+
text?: string;
|
|
5
9
|
valueSourceSelector?: string;
|
|
6
10
|
minChars?: number;
|
|
7
11
|
maxChars?: number;
|
|
8
12
|
label?: string;
|
|
13
|
+
rewriteField?: "summary" | "generic" | string;
|
|
14
|
+
gatewayOp?: string;
|
|
15
|
+
tone?: "professional" | string;
|
|
9
16
|
onAppendEvent?: string;
|
|
10
17
|
onReplaceEvent?: string;
|
|
11
18
|
[key: string]: any;
|
|
12
19
|
}
|
|
13
20
|
|
|
14
21
|
const {
|
|
22
|
+
mode = "suggestions",
|
|
15
23
|
featureKey,
|
|
16
24
|
value,
|
|
25
|
+
text,
|
|
17
26
|
valueSourceSelector,
|
|
18
27
|
minChars = 30,
|
|
19
28
|
maxChars = 1500,
|
|
20
29
|
label = "AI",
|
|
30
|
+
rewriteField = "generic",
|
|
31
|
+
gatewayOp = "rewrite",
|
|
32
|
+
tone = "professional",
|
|
21
33
|
onAppendEvent = "av:ai-append",
|
|
22
34
|
onReplaceEvent = "av:ai-replace",
|
|
23
35
|
...rest
|
|
24
36
|
} = Astro.props as Props;
|
|
25
37
|
|
|
26
38
|
const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
39
|
+
const initialValue = typeof text === "string" ? text : (value ?? "");
|
|
27
40
|
---
|
|
28
41
|
|
|
29
42
|
<div
|
|
30
43
|
class="av-ai-assist"
|
|
31
44
|
data-av-ai-assist={componentId}
|
|
32
|
-
data-value={
|
|
45
|
+
data-value={initialValue}
|
|
33
46
|
{...rest}
|
|
34
47
|
x-data={`avAiAssist(${JSON.stringify({
|
|
35
|
-
|
|
48
|
+
mode,
|
|
49
|
+
featureKey: featureKey ?? null,
|
|
36
50
|
valueSourceSelector: valueSourceSelector ?? null,
|
|
37
51
|
minChars,
|
|
38
52
|
maxChars,
|
|
39
53
|
label,
|
|
54
|
+
rewriteField,
|
|
55
|
+
gatewayOp,
|
|
56
|
+
tone,
|
|
40
57
|
onAppendEvent,
|
|
41
58
|
onReplaceEvent,
|
|
42
59
|
})})`}
|
|
43
60
|
x-init="init()"
|
|
44
61
|
>
|
|
45
|
-
<span class="av-ai-assist-trigger" :title="disabledReason ||
|
|
62
|
+
<span class="av-ai-assist-trigger" :title="disabledReason || buttonTitle">
|
|
46
63
|
<button
|
|
47
64
|
type="button"
|
|
48
65
|
class="av-btn-ghost av-btn-sm av-ai-assist-btn"
|
|
49
66
|
@click.prevent="openModal()"
|
|
50
67
|
:disabled="!canFetch"
|
|
51
|
-
aria-label="
|
|
68
|
+
:aria-label="buttonAriaLabel"
|
|
52
69
|
>
|
|
53
70
|
<span aria-hidden="true">✨</span>
|
|
54
71
|
<span x-text="label"></span>
|
|
@@ -57,15 +74,15 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
57
74
|
|
|
58
75
|
<template x-if="open">
|
|
59
76
|
<div class="av-ai-assist-overlay" @click.self="closeModal()" @keydown.escape.window="closeModal()">
|
|
60
|
-
<div class="av-ai-assist-modal" role="dialog" aria-modal="true" aria-label="
|
|
77
|
+
<div class="av-ai-assist-modal" role="dialog" aria-modal="true" :aria-label="modalAriaLabel">
|
|
61
78
|
<div class="av-auth-stack-sm">
|
|
62
79
|
<div class="av-ai-assist-modal-header">
|
|
63
|
-
<h3 class="av-card-heading av-m-0"
|
|
80
|
+
<h3 class="av-card-heading av-m-0" x-text="modalTitle"></h3>
|
|
64
81
|
<button
|
|
65
82
|
type="button"
|
|
66
83
|
class="av-btn-ghost av-btn-sm av-ai-assist-close"
|
|
67
84
|
@click.prevent="closeModal()"
|
|
68
|
-
aria-label="
|
|
85
|
+
:aria-label="closeAriaLabel"
|
|
69
86
|
title="Close"
|
|
70
87
|
>
|
|
71
88
|
<span aria-hidden="true">X</span>
|
|
@@ -73,14 +90,24 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
73
90
|
</div>
|
|
74
91
|
|
|
75
92
|
<template x-if="loading">
|
|
76
|
-
<p class="av-text-soft av-m-0"
|
|
93
|
+
<p class="av-text-soft av-m-0" x-text="loadingLabel"></p>
|
|
77
94
|
</template>
|
|
78
95
|
|
|
79
96
|
<template x-if="error && !loading">
|
|
80
97
|
<div class="av-alert av-alert-danger" role="status" x-text="error"></div>
|
|
81
98
|
</template>
|
|
82
99
|
|
|
83
|
-
<template x-if="!loading &&
|
|
100
|
+
<template x-if="isRewriteMode() && !loading && rewrittenText">
|
|
101
|
+
<div class="av-auth-stack-xs">
|
|
102
|
+
<textarea class="av-input av-ai-assist-rewrite-output" readonly x-text="rewrittenText"></textarea>
|
|
103
|
+
<div class="av-row-wrap">
|
|
104
|
+
<button type="button" class="av-btn-primary av-btn-sm" @click.prevent="replaceSuggestion(rewrittenText)">Replace</button>
|
|
105
|
+
<button type="button" class="av-btn-ghost av-btn-sm" @click.prevent="closeModal()">Close</button>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<template x-if="!isRewriteMode() && !loading && suggestions.length">
|
|
84
111
|
<div class="av-auth-stack-xs">
|
|
85
112
|
<template x-for="(suggestion, index) in suggestions" :key="`assist-${index}`">
|
|
86
113
|
<div class="av-ai-assist-suggestion">
|
|
@@ -97,7 +124,7 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
97
124
|
</div>
|
|
98
125
|
</template>
|
|
99
126
|
|
|
100
|
-
<p class="av-form-hint av-m-0"
|
|
127
|
+
<p class="av-form-hint av-m-0" x-text="footerHint"></p>
|
|
101
128
|
</div>
|
|
102
129
|
</div>
|
|
103
130
|
</div>
|
|
@@ -107,7 +134,8 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
107
134
|
<script is:inline>
|
|
108
135
|
if (typeof window !== "undefined" && !window.avAiAssist) {
|
|
109
136
|
window.avAiAssist = (config) => ({
|
|
110
|
-
|
|
137
|
+
mode: config.mode === "rewrite" ? "rewrite" : "suggestions",
|
|
138
|
+
featureKey: typeof config.featureKey === "string" ? config.featureKey : null,
|
|
111
139
|
valueSourceSelector:
|
|
112
140
|
typeof config.valueSourceSelector === "string" && config.valueSourceSelector.trim()
|
|
113
141
|
? config.valueSourceSelector.trim()
|
|
@@ -115,6 +143,12 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
115
143
|
minChars: Number(config.minChars ?? 30),
|
|
116
144
|
maxChars: Number(config.maxChars ?? 1500),
|
|
117
145
|
label: typeof config.label === "string" && config.label ? config.label : "AI",
|
|
146
|
+
rewriteField:
|
|
147
|
+
typeof config.rewriteField === "string" && config.rewriteField.trim()
|
|
148
|
+
? config.rewriteField.trim()
|
|
149
|
+
: "generic",
|
|
150
|
+
gatewayOp: typeof config.gatewayOp === "string" && config.gatewayOp ? config.gatewayOp : "rewrite",
|
|
151
|
+
tone: typeof config.tone === "string" && config.tone ? config.tone : "professional",
|
|
118
152
|
onAppendEvent: typeof config.onAppendEvent === "string" && config.onAppendEvent ? config.onAppendEvent : "av:ai-append",
|
|
119
153
|
onReplaceEvent: typeof config.onReplaceEvent === "string" && config.onReplaceEvent ? config.onReplaceEvent : "av:ai-replace",
|
|
120
154
|
value: "",
|
|
@@ -122,6 +156,7 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
122
156
|
loading: false,
|
|
123
157
|
error: null,
|
|
124
158
|
suggestions: [],
|
|
159
|
+
rewrittenText: "",
|
|
125
160
|
copiedIndex: null,
|
|
126
161
|
observer: null,
|
|
127
162
|
sourceElement: null,
|
|
@@ -143,6 +178,10 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
143
178
|
}
|
|
144
179
|
},
|
|
145
180
|
|
|
181
|
+
isRewriteMode() {
|
|
182
|
+
return this.mode === "rewrite";
|
|
183
|
+
},
|
|
184
|
+
|
|
146
185
|
get textValue() {
|
|
147
186
|
const raw = typeof this.value === "string" ? this.value : "";
|
|
148
187
|
return raw.trim();
|
|
@@ -152,8 +191,42 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
152
191
|
return !this.loading && !this.disabledReason;
|
|
153
192
|
},
|
|
154
193
|
|
|
194
|
+
get buttonTitle() {
|
|
195
|
+
return this.isRewriteMode() ? "Rewrite with AI" : `Get ${this.label} suggestions`;
|
|
196
|
+
},
|
|
197
|
+
|
|
198
|
+
get buttonAriaLabel() {
|
|
199
|
+
return this.isRewriteMode() ? "Rewrite with AI" : "Get AI suggestions";
|
|
200
|
+
},
|
|
201
|
+
|
|
202
|
+
get modalTitle() {
|
|
203
|
+
return this.isRewriteMode() ? "AI Rewrite" : "AI Suggestions";
|
|
204
|
+
},
|
|
205
|
+
|
|
206
|
+
get modalAriaLabel() {
|
|
207
|
+
return this.isRewriteMode() ? "AI rewrite" : "AI suggestions";
|
|
208
|
+
},
|
|
209
|
+
|
|
210
|
+
get closeAriaLabel() {
|
|
211
|
+
return this.isRewriteMode() ? "Close AI rewrite" : "Close AI suggestions";
|
|
212
|
+
},
|
|
213
|
+
|
|
214
|
+
get loadingLabel() {
|
|
215
|
+
return this.isRewriteMode() ? "Rewriting text..." : "Generating suggestions...";
|
|
216
|
+
},
|
|
217
|
+
|
|
218
|
+
get footerHint() {
|
|
219
|
+
return this.isRewriteMode()
|
|
220
|
+
? "AI rewrite is optional. Please review before saving."
|
|
221
|
+
: "AI suggestions are optional. Please review before saving.";
|
|
222
|
+
},
|
|
223
|
+
|
|
155
224
|
get disabledReason() {
|
|
156
|
-
if (this.loading) return
|
|
225
|
+
if (this.loading) return this.loadingLabel;
|
|
226
|
+
if (this.isRewriteMode()) {
|
|
227
|
+
if (this.textValue.length === 0) return "Add text to rewrite.";
|
|
228
|
+
return "";
|
|
229
|
+
}
|
|
157
230
|
if (this.textValue.length > this.maxChars) return `Text exceeds ${this.maxChars} characters.`;
|
|
158
231
|
if (this.textValue.length < this.minChars) return `Add at least ${this.minChars} characters to get meaningful suggestions.`;
|
|
159
232
|
return "";
|
|
@@ -174,6 +247,7 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
174
247
|
this.loading = false;
|
|
175
248
|
this.error = null;
|
|
176
249
|
this.suggestions = [];
|
|
250
|
+
this.rewrittenText = "";
|
|
177
251
|
this.copiedIndex = null;
|
|
178
252
|
},
|
|
179
253
|
|
|
@@ -197,19 +271,30 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
197
271
|
this.loading = true;
|
|
198
272
|
this.error = null;
|
|
199
273
|
this.suggestions = [];
|
|
274
|
+
this.rewrittenText = "";
|
|
200
275
|
this.copiedIndex = null;
|
|
201
276
|
|
|
202
277
|
try {
|
|
278
|
+
const body = this.isRewriteMode()
|
|
279
|
+
? {
|
|
280
|
+
op: this.gatewayOp || "rewrite",
|
|
281
|
+
text: this.textValue,
|
|
282
|
+
field: this.rewriteField || "generic",
|
|
283
|
+
maxChars: Number.isFinite(this.maxChars) ? this.maxChars : undefined,
|
|
284
|
+
tone: this.tone || "professional",
|
|
285
|
+
}
|
|
286
|
+
: {
|
|
287
|
+
featureKey: this.featureKey,
|
|
288
|
+
userText: this.textValue,
|
|
289
|
+
};
|
|
290
|
+
|
|
203
291
|
const response = await fetch("/api/ai/suggest", {
|
|
204
292
|
method: "POST",
|
|
205
293
|
credentials: "include",
|
|
206
294
|
headers: {
|
|
207
295
|
"Content-Type": "application/json",
|
|
208
296
|
},
|
|
209
|
-
body: JSON.stringify(
|
|
210
|
-
featureKey: this.featureKey,
|
|
211
|
-
userText: this.textValue,
|
|
212
|
-
}),
|
|
297
|
+
body: JSON.stringify(body),
|
|
213
298
|
});
|
|
214
299
|
|
|
215
300
|
const text = await response.text();
|
|
@@ -228,6 +313,15 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
228
313
|
return;
|
|
229
314
|
}
|
|
230
315
|
|
|
316
|
+
if (this.isRewriteMode()) {
|
|
317
|
+
const rewritten = typeof payload?.text === "string" ? payload.text.trim() : "";
|
|
318
|
+
this.rewrittenText = rewritten;
|
|
319
|
+
if (!this.rewrittenText) {
|
|
320
|
+
this.error = "No rewrite returned.";
|
|
321
|
+
}
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
231
325
|
const list = Array.isArray(payload?.suggestions)
|
|
232
326
|
? payload.suggestions
|
|
233
327
|
.filter((item) => typeof item === "string")
|
|
@@ -335,4 +429,9 @@ const componentId = `av-ai-assist-${Math.random().toString(36).slice(2, 10)}`;
|
|
|
335
429
|
background: var(--av-surface-soft, rgba(255, 255, 255, 0.04));
|
|
336
430
|
padding: 0.75rem;
|
|
337
431
|
}
|
|
432
|
+
|
|
433
|
+
.av-ai-assist-rewrite-output {
|
|
434
|
+
min-height: 10rem;
|
|
435
|
+
resize: none;
|
|
436
|
+
}
|
|
338
437
|
</style>
|
|
@@ -151,7 +151,7 @@ const hasDeclaration = Boolean(declaration.text || declaration.place || declarat
|
|
|
151
151
|
return (
|
|
152
152
|
<div class="av-print-avoid-break flex flex-col gap-1 sm:flex-row sm:items-baseline sm:justify-between">
|
|
153
153
|
<div>
|
|
154
|
-
{edu.degree ? <h3 class="text-base font-semibold">{edu.degree}</h3> : null}
|
|
154
|
+
{edu.degree ? <h3 class="text-base font-semibold text-slate-900">{edu.degree}</h3> : null}
|
|
155
155
|
<p class="text-sm text-slate-700">{edu.school}</p>
|
|
156
156
|
{edu.location && (
|
|
157
157
|
<p class="text-sm text-slate-600">{edu.location}</p>
|