@actuate-media/cms-core 0.76.3 → 0.77.1
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/CHANGELOG.md +15 -0
- package/dist/__tests__/ai/ai.test.js +6 -1
- package/dist/__tests__/ai/ai.test.js.map +1 -1
- package/dist/__tests__/ai/brand-voice-alignment.test.d.ts +2 -0
- package/dist/__tests__/ai/brand-voice-alignment.test.d.ts.map +1 -0
- package/dist/__tests__/ai/brand-voice-alignment.test.js +38 -0
- package/dist/__tests__/ai/brand-voice-alignment.test.js.map +1 -0
- package/dist/__tests__/ai/brand-voice-analyze.test.d.ts +2 -0
- package/dist/__tests__/ai/brand-voice-analyze.test.d.ts.map +1 -0
- package/dist/__tests__/ai/brand-voice-analyze.test.js +117 -0
- package/dist/__tests__/ai/brand-voice-analyze.test.js.map +1 -0
- package/dist/__tests__/ai/brand-voice-document.test.d.ts +2 -0
- package/dist/__tests__/ai/brand-voice-document.test.d.ts.map +1 -0
- package/dist/__tests__/ai/brand-voice-document.test.js +74 -0
- package/dist/__tests__/ai/brand-voice-document.test.js.map +1 -0
- package/dist/__tests__/ai/brand-voice-fix.test.d.ts +2 -0
- package/dist/__tests__/ai/brand-voice-fix.test.d.ts.map +1 -0
- package/dist/__tests__/ai/brand-voice-fix.test.js +34 -0
- package/dist/__tests__/ai/brand-voice-fix.test.js.map +1 -0
- package/dist/__tests__/ai/brand-voice-sources.test.d.ts +2 -0
- package/dist/__tests__/ai/brand-voice-sources.test.d.ts.map +1 -0
- package/dist/__tests__/ai/brand-voice-sources.test.js +14 -0
- package/dist/__tests__/ai/brand-voice-sources.test.js.map +1 -0
- package/dist/__tests__/ai/brand-voice-structured.test.d.ts +2 -0
- package/dist/__tests__/ai/brand-voice-structured.test.d.ts.map +1 -0
- package/dist/__tests__/ai/brand-voice-structured.test.js +49 -0
- package/dist/__tests__/ai/brand-voice-structured.test.js.map +1 -0
- package/dist/__tests__/ai/content-writing-generate.test.d.ts +2 -0
- package/dist/__tests__/ai/content-writing-generate.test.d.ts.map +1 -0
- package/dist/__tests__/ai/content-writing-generate.test.js +38 -0
- package/dist/__tests__/ai/content-writing-generate.test.js.map +1 -0
- package/dist/__tests__/ai/runtime.test.js +45 -0
- package/dist/__tests__/ai/runtime.test.js.map +1 -1
- package/dist/__tests__/api/route-contracts.test.js +3 -0
- package/dist/__tests__/api/route-contracts.test.js.map +1 -1
- package/dist/ai/brand-voice-alignment.d.ts +17 -0
- package/dist/ai/brand-voice-alignment.d.ts.map +1 -0
- package/dist/ai/brand-voice-alignment.js +40 -0
- package/dist/ai/brand-voice-alignment.js.map +1 -0
- package/dist/ai/brand-voice-analyze.d.ts +71 -0
- package/dist/ai/brand-voice-analyze.d.ts.map +1 -0
- package/dist/ai/brand-voice-analyze.js +151 -0
- package/dist/ai/brand-voice-analyze.js.map +1 -0
- package/dist/ai/brand-voice-document.d.ts +56 -0
- package/dist/ai/brand-voice-document.d.ts.map +1 -0
- package/dist/ai/brand-voice-document.js +335 -0
- package/dist/ai/brand-voice-document.js.map +1 -0
- package/dist/ai/brand-voice-fix.d.ts +26 -0
- package/dist/ai/brand-voice-fix.d.ts.map +1 -0
- package/dist/ai/brand-voice-fix.js +97 -0
- package/dist/ai/brand-voice-fix.js.map +1 -0
- package/dist/ai/brand-voice-save.d.ts +11 -0
- package/dist/ai/brand-voice-save.d.ts.map +1 -0
- package/dist/ai/brand-voice-save.js +51 -0
- package/dist/ai/brand-voice-save.js.map +1 -0
- package/dist/ai/brand-voice-sources.d.ts +16 -0
- package/dist/ai/brand-voice-sources.d.ts.map +1 -0
- package/dist/ai/brand-voice-sources.js +106 -0
- package/dist/ai/brand-voice-sources.js.map +1 -0
- package/dist/ai/brand-voice-structured.d.ts +87 -0
- package/dist/ai/brand-voice-structured.d.ts.map +1 -0
- package/dist/ai/brand-voice-structured.js +438 -0
- package/dist/ai/brand-voice-structured.js.map +1 -0
- package/dist/ai/brand-voice.d.ts +3 -0
- package/dist/ai/brand-voice.d.ts.map +1 -1
- package/dist/ai/brand-voice.js +18 -0
- package/dist/ai/brand-voice.js.map +1 -1
- package/dist/ai/catalog.d.ts +6 -0
- package/dist/ai/catalog.d.ts.map +1 -1
- package/dist/ai/catalog.js +36 -6
- package/dist/ai/catalog.js.map +1 -1
- package/dist/ai/content-writing-generate.d.ts +19 -0
- package/dist/ai/content-writing-generate.d.ts.map +1 -0
- package/dist/ai/content-writing-generate.js +64 -0
- package/dist/ai/content-writing-generate.js.map +1 -0
- package/dist/ai/index.d.ts +8 -0
- package/dist/ai/index.d.ts.map +1 -1
- package/dist/ai/index.js +8 -0
- package/dist/ai/index.js.map +1 -1
- package/dist/ai/models.d.ts.map +1 -1
- package/dist/ai/models.js +2 -2
- package/dist/ai/models.js.map +1 -1
- package/dist/ai/providers/anthropic.js +2 -2
- package/dist/ai/providers/anthropic.js.map +1 -1
- package/dist/ai/providers/http.d.ts +7 -3
- package/dist/ai/providers/http.d.ts.map +1 -1
- package/dist/ai/providers/http.js +48 -4
- package/dist/ai/providers/http.js.map +1 -1
- package/dist/ai/resolve.d.ts.map +1 -1
- package/dist/ai/resolve.js +2 -1
- package/dist/ai/resolve.js.map +1 -1
- package/dist/ai/runtime.d.ts.map +1 -1
- package/dist/ai/runtime.js +2 -1
- package/dist/ai/runtime.js.map +1 -1
- package/dist/ai/types.d.ts +8 -0
- package/dist/ai/types.d.ts.map +1 -1
- package/dist/api/routes/ai-seo.d.ts.map +1 -1
- package/dist/api/routes/ai-seo.js +22 -3
- package/dist/api/routes/ai-seo.js.map +1 -1
- package/dist/api/routes/ai-settings.d.ts.map +1 -1
- package/dist/api/routes/ai-settings.js +260 -7
- package/dist/api/routes/ai-settings.js.map +1 -1
- package/dist/api/routes/saved-sections.d.ts.map +1 -1
- package/dist/api/routes/saved-sections.js +4 -1
- package/dist/api/routes/saved-sections.js.map +1 -1
- package/dist/brand-voice/index.d.ts +7 -0
- package/dist/brand-voice/index.d.ts.map +1 -0
- package/dist/brand-voice/index.js +7 -0
- package/dist/brand-voice/index.js.map +1 -0
- package/package.json +6 -1
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Brand voice profile document — portable markdown + YAML frontmatter format.
|
|
3
|
+
*
|
|
4
|
+
* Pure utilities (no Node APIs) shared by the admin UI and server normalization.
|
|
5
|
+
* The markdown file is the interchange format; flat form fields are derived from
|
|
6
|
+
* it for prompt injection and the quick-edit UI.
|
|
7
|
+
*/
|
|
8
|
+
import { applyStructuredProfileToDocument } from './brand-voice-structured.js';
|
|
9
|
+
export const BRAND_VOICE_DOCUMENT_MAX = 50_000;
|
|
10
|
+
function escapeYamlString(value) {
|
|
11
|
+
if (/[:#{}[\],&*?|>-]/.test(value) || value.includes('"')) {
|
|
12
|
+
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
13
|
+
}
|
|
14
|
+
return value.includes('\n') ? `"${value.replace(/"/g, '\\"')}"` : value;
|
|
15
|
+
}
|
|
16
|
+
function yamlList(items) {
|
|
17
|
+
if (items.length === 0)
|
|
18
|
+
return ' []';
|
|
19
|
+
return items.map((item) => ` - ${escapeYamlString(item)}`).join('\n');
|
|
20
|
+
}
|
|
21
|
+
/** Split a markdown file into YAML frontmatter and body. */
|
|
22
|
+
export function splitBrandVoiceDocument(markdown) {
|
|
23
|
+
const trimmed = markdown.trim();
|
|
24
|
+
const match = /^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/.exec(trimmed);
|
|
25
|
+
if (!match)
|
|
26
|
+
return null;
|
|
27
|
+
return { frontmatter: match[1].trim(), body: match[2].trim() };
|
|
28
|
+
}
|
|
29
|
+
function readScalar(frontmatter, key) {
|
|
30
|
+
const re = new RegExp(`^\\s*${key}:\\s*(.+?)\\s*(?:#.*)?$`, 'm');
|
|
31
|
+
const match = re.exec(frontmatter);
|
|
32
|
+
if (!match)
|
|
33
|
+
return '';
|
|
34
|
+
return match[1].replace(/^["']|["']$/g, '').trim();
|
|
35
|
+
}
|
|
36
|
+
/** Next top-level frontmatter line (unindented key, comment, or EOF). */
|
|
37
|
+
const FRONTMATTER_SECTION_END = '(?=^[^\\s#].|^#|\\Z)';
|
|
38
|
+
function readNestedScalar(frontmatter, section, key) {
|
|
39
|
+
const sectionRe = new RegExp(`^${section}:\\s*\\n([\\s\\S]*?)${FRONTMATTER_SECTION_END}`, 'm');
|
|
40
|
+
const sectionMatch = sectionRe.exec(frontmatter);
|
|
41
|
+
if (!sectionMatch)
|
|
42
|
+
return '';
|
|
43
|
+
return readScalar(sectionMatch[1], key);
|
|
44
|
+
}
|
|
45
|
+
function readYamlList(frontmatter, key) {
|
|
46
|
+
const re = new RegExp(`^\\s*${key}:\\s*\\n((?:\\s+-\\s+.+\n?)*)`, 'm');
|
|
47
|
+
const match = re.exec(frontmatter);
|
|
48
|
+
if (!match)
|
|
49
|
+
return [];
|
|
50
|
+
return [...match[1].matchAll(/^\s+-\s+(.+)\s*$/gm)]
|
|
51
|
+
.map((m) => m[1].replace(/^["']|["']$/g, '').trim())
|
|
52
|
+
.filter(Boolean);
|
|
53
|
+
}
|
|
54
|
+
function readMarkdownSection(body, heading) {
|
|
55
|
+
const re = new RegExp(`^##\\s+${heading.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\s*$\\n([\\s\\S]*?)(?=^##\\s+|\\Z)`, 'im');
|
|
56
|
+
const match = re.exec(body);
|
|
57
|
+
if (!match)
|
|
58
|
+
return '';
|
|
59
|
+
return match[1]
|
|
60
|
+
.replace(/^>\s.*$/gm, '')
|
|
61
|
+
.replace(/^\*.*\*$/gm, '')
|
|
62
|
+
.trim();
|
|
63
|
+
}
|
|
64
|
+
function extractWorkedExampleSample(body) {
|
|
65
|
+
const section = readMarkdownSection(body, 'Worked examples');
|
|
66
|
+
if (!section)
|
|
67
|
+
return '';
|
|
68
|
+
const row = section
|
|
69
|
+
.split('\n')
|
|
70
|
+
.map((line) => line.trim())
|
|
71
|
+
.find((line) => line.startsWith('|') && !line.includes('Off-brand') && !line.match(/^\|[-| ]+\|$/));
|
|
72
|
+
if (!row)
|
|
73
|
+
return '';
|
|
74
|
+
const cells = row
|
|
75
|
+
.split('|')
|
|
76
|
+
.map((c) => c.trim())
|
|
77
|
+
.filter(Boolean);
|
|
78
|
+
return cells[1]?.replace(/^["']|["']$/g, '') ?? '';
|
|
79
|
+
}
|
|
80
|
+
/** Derive quick-edit form fields from a profile document body + frontmatter. */
|
|
81
|
+
export function deriveBrandVoiceFields(body, frontmatter) {
|
|
82
|
+
const name = readNestedScalar(frontmatter, 'profile', 'brand_name');
|
|
83
|
+
const voiceLine = readMarkdownSection(body, 'Voice in one line');
|
|
84
|
+
const persona = readMarkdownSection(body, 'Persona');
|
|
85
|
+
const toneParts = [voiceLine, persona].filter(Boolean);
|
|
86
|
+
const audience = readMarkdownSection(body, 'Audience');
|
|
87
|
+
const doSection = readMarkdownSection(body, 'Do');
|
|
88
|
+
const dontSection = readMarkdownSection(body, "Don't");
|
|
89
|
+
const guidelinesParts = [];
|
|
90
|
+
if (doSection)
|
|
91
|
+
guidelinesParts.push(`Do:\n${doSection}`);
|
|
92
|
+
if (dontSection)
|
|
93
|
+
guidelinesParts.push(`Don't:\n${dontSection}`);
|
|
94
|
+
const sampleFromTable = extractWorkedExampleSample(body);
|
|
95
|
+
return {
|
|
96
|
+
name,
|
|
97
|
+
tone: toneParts.join('\n\n'),
|
|
98
|
+
audience,
|
|
99
|
+
guidelines: guidelinesParts.join('\n\n'),
|
|
100
|
+
sampleText: sampleFromTable,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function parseMeta(frontmatter) {
|
|
104
|
+
const versionRaw = readNestedScalar(frontmatter, 'profile', 'version');
|
|
105
|
+
const version = Number.parseInt(versionRaw, 10);
|
|
106
|
+
const statusRaw = readNestedScalar(frontmatter, 'profile', 'status');
|
|
107
|
+
const status = statusRaw === 'reviewed' || statusRaw === 'approved' ? statusRaw : 'draft';
|
|
108
|
+
const profileSection = new RegExp(`^profile:\\s*\\n([\\s\\S]*?)${FRONTMATTER_SECTION_END}`, 'm').exec(frontmatter)?.[1] ?? '';
|
|
109
|
+
const generatedFrom = readYamlList(profileSection, 'generated_from');
|
|
110
|
+
const lastUpdated = readNestedScalar(frontmatter, 'profile', 'last_updated') ||
|
|
111
|
+
new Date().toISOString().slice(0, 10);
|
|
112
|
+
return {
|
|
113
|
+
version: Number.isFinite(version) && version > 0 ? version : 1,
|
|
114
|
+
status,
|
|
115
|
+
generatedFrom,
|
|
116
|
+
lastUpdated,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
export function parseBrandVoiceDocument(markdown) {
|
|
120
|
+
const raw = markdown.trim();
|
|
121
|
+
if (!raw)
|
|
122
|
+
return { ok: false, error: 'Profile document is empty.' };
|
|
123
|
+
if (raw.length > BRAND_VOICE_DOCUMENT_MAX) {
|
|
124
|
+
return { ok: false, error: `Profile document exceeds ${BRAND_VOICE_DOCUMENT_MAX} characters.` };
|
|
125
|
+
}
|
|
126
|
+
const split = splitBrandVoiceDocument(raw);
|
|
127
|
+
if (!split) {
|
|
128
|
+
return { ok: false, error: 'Profile must start with YAML frontmatter (--- ... ---).' };
|
|
129
|
+
}
|
|
130
|
+
const meta = parseMeta(split.frontmatter);
|
|
131
|
+
const fields = deriveBrandVoiceFields(split.body, split.frontmatter);
|
|
132
|
+
if (!fields.name.trim() && !fields.tone.trim() && !(fields.guidelines ?? '').trim()) {
|
|
133
|
+
return {
|
|
134
|
+
ok: false,
|
|
135
|
+
error: "Profile document must include a brand name, tone, or Do/Don't guidance.",
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
ok: true,
|
|
140
|
+
value: {
|
|
141
|
+
document: raw,
|
|
142
|
+
frontmatter: split.frontmatter,
|
|
143
|
+
body: split.body,
|
|
144
|
+
meta,
|
|
145
|
+
fields,
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function defaultFrontmatter(meta, name) {
|
|
150
|
+
return `# ─────────────────────────────────────────────────────────────────────
|
|
151
|
+
# MACHINE-READABLE RULES
|
|
152
|
+
# The analyze-and-fix engine parses this frontmatter directly.
|
|
153
|
+
# Everything here is deterministic — testable, enforceable, explainable.
|
|
154
|
+
# ─────────────────────────────────────────────────────────────────────
|
|
155
|
+
|
|
156
|
+
profile:
|
|
157
|
+
brand_name: ${escapeYamlString(name || 'Brand voice')}
|
|
158
|
+
version: ${meta.version}
|
|
159
|
+
generated_from:
|
|
160
|
+
${yamlList(meta.generatedFrom)}
|
|
161
|
+
last_updated: ${escapeYamlString(meta.lastUpdated)}
|
|
162
|
+
status: ${meta.status}
|
|
163
|
+
|
|
164
|
+
tone:
|
|
165
|
+
formal_casual: 50
|
|
166
|
+
serious_playful: 50
|
|
167
|
+
concise_detailed: 50
|
|
168
|
+
reserved_bold: 50
|
|
169
|
+
reading_level: 8
|
|
170
|
+
|
|
171
|
+
mechanics:
|
|
172
|
+
oxford_comma: true
|
|
173
|
+
contractions: allow
|
|
174
|
+
exclamation_points: avoid
|
|
175
|
+
emoji: forbid
|
|
176
|
+
case_headlines: sentence
|
|
177
|
+
case_buttons: sentence
|
|
178
|
+
numbers: numerals_for_10_plus
|
|
179
|
+
serial_spelling: us
|
|
180
|
+
|
|
181
|
+
vocabulary:
|
|
182
|
+
banned: []
|
|
183
|
+
preferred: []
|
|
184
|
+
avoid_patterns: []
|
|
185
|
+
|
|
186
|
+
channels:
|
|
187
|
+
meta_description:
|
|
188
|
+
max_chars: 160
|
|
189
|
+
note: "lead with the benefit; one clear action"
|
|
190
|
+
blog:
|
|
191
|
+
note: "authoritative; still no hype words"`;
|
|
192
|
+
}
|
|
193
|
+
function defaultBody(fields) {
|
|
194
|
+
const voiceLine = fields.tone.split('\n\n')[0]?.trim() ?? fields.tone.trim();
|
|
195
|
+
const persona = fields.tone.includes('\n\n')
|
|
196
|
+
? fields.tone.split('\n\n').slice(1).join('\n\n').trim()
|
|
197
|
+
: '';
|
|
198
|
+
const doLines = (fields.guidelines ?? '')
|
|
199
|
+
.split(/\n?Don't:\n/i)[0]
|
|
200
|
+
?.replace(/^Do:\n?/i, '')
|
|
201
|
+
.trim();
|
|
202
|
+
const dontLines = (fields.guidelines ?? '').split(/\n?Don't:\n/i)[1]?.trim() ?? '';
|
|
203
|
+
const sample = (fields.sampleText ?? '').trim();
|
|
204
|
+
return `# Brand Voice — Human Guidance
|
|
205
|
+
|
|
206
|
+
> This half of the file is read by the engine as context and by your team as
|
|
207
|
+
> the source of truth. Edit freely. The frontmatter above is what gets enforced;
|
|
208
|
+
> this prose is what makes the enforcement *make sense*.
|
|
209
|
+
|
|
210
|
+
## Voice in one line
|
|
211
|
+
${voiceLine || '_Describe how the brand sounds at its best._'}
|
|
212
|
+
|
|
213
|
+
## Persona
|
|
214
|
+
${persona || '_Who the brand sounds like — and who it does not._'}
|
|
215
|
+
|
|
216
|
+
## Audience
|
|
217
|
+
${(fields.audience ?? '').trim() || '_Who is being spoken to, and what they care about._'}
|
|
218
|
+
|
|
219
|
+
## Do
|
|
220
|
+
${doLines || '- Lead with the benefit or the point; front-load the value.'}
|
|
221
|
+
|
|
222
|
+
## Don't
|
|
223
|
+
${dontLines || '- No hype words or stacked superlatives.'}
|
|
224
|
+
|
|
225
|
+
## Worked examples
|
|
226
|
+
| Off-brand | On-brand | Why |
|
|
227
|
+
|---|---|---|
|
|
228
|
+
${sample ? `| _Before_ | "${sample.replace(/"/g, '\\"')}" | Representative on-brand sample. |` : '| _Off-brand example_ | _On-brand rewrite_ | _Why the change helps._ |'}
|
|
229
|
+
|
|
230
|
+
## Notes / change log
|
|
231
|
+
- v1 — profile created in Actuate CMS.`;
|
|
232
|
+
}
|
|
233
|
+
/** Serialize form fields (and optional metadata) into the portable markdown file. */
|
|
234
|
+
export function serializeBrandVoiceDocument(input) {
|
|
235
|
+
const existing = input.existingDocument?.trim();
|
|
236
|
+
if (existing) {
|
|
237
|
+
const split = splitBrandVoiceDocument(existing);
|
|
238
|
+
if (split) {
|
|
239
|
+
const meta = {
|
|
240
|
+
...parseMeta(split.frontmatter),
|
|
241
|
+
...input.meta,
|
|
242
|
+
lastUpdated: new Date().toISOString().slice(0, 10),
|
|
243
|
+
};
|
|
244
|
+
if (input.meta?.version == null)
|
|
245
|
+
meta.version += 1;
|
|
246
|
+
let document = `---\n${mergeFrontmatterProfile(split.frontmatter, input.fields, meta)}\n---\n\n${defaultBody(input.fields)}`;
|
|
247
|
+
if (input.structured) {
|
|
248
|
+
document = applyStructuredProfileToDocument(document, input.structured);
|
|
249
|
+
}
|
|
250
|
+
return document.slice(0, BRAND_VOICE_DOCUMENT_MAX);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const meta = {
|
|
254
|
+
version: input.meta?.version ?? 1,
|
|
255
|
+
status: input.meta?.status ?? 'draft',
|
|
256
|
+
generatedFrom: input.meta?.generatedFrom ?? [],
|
|
257
|
+
lastUpdated: input.meta?.lastUpdated ?? new Date().toISOString().slice(0, 10),
|
|
258
|
+
};
|
|
259
|
+
let document = `---\n${defaultFrontmatter(meta, input.fields.name)}\n---\n\n${defaultBody(input.fields)}`;
|
|
260
|
+
if (input.structured) {
|
|
261
|
+
document = applyStructuredProfileToDocument(document, input.structured);
|
|
262
|
+
}
|
|
263
|
+
return document.slice(0, BRAND_VOICE_DOCUMENT_MAX);
|
|
264
|
+
}
|
|
265
|
+
function mergeFrontmatterProfile(frontmatter, fields, meta) {
|
|
266
|
+
let next = frontmatter;
|
|
267
|
+
const setNested = (section, key, value) => {
|
|
268
|
+
const sectionRe = new RegExp(`(^${section}:\\s*\\n)([\\s\\S]*?)${FRONTMATTER_SECTION_END}`, 'm');
|
|
269
|
+
const match = sectionRe.exec(next);
|
|
270
|
+
if (!match)
|
|
271
|
+
return;
|
|
272
|
+
const block = match[2];
|
|
273
|
+
const keyRe = new RegExp(`^(\\s*${key}:\\s*).*$`, 'm');
|
|
274
|
+
if (keyRe.test(block)) {
|
|
275
|
+
next = next.replace(sectionRe, (_all, head, body) => {
|
|
276
|
+
const updatedBody = body.replace(keyRe, `$1${value}`);
|
|
277
|
+
return `${head}${updatedBody}`;
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
setNested('profile', 'brand_name', escapeYamlString(fields.name || 'Brand voice'));
|
|
282
|
+
setNested('profile', 'version', meta.version);
|
|
283
|
+
setNested('profile', 'status', meta.status);
|
|
284
|
+
setNested('profile', 'last_updated', escapeYamlString(meta.lastUpdated));
|
|
285
|
+
return next;
|
|
286
|
+
}
|
|
287
|
+
/** Merge parsed document fields into a profile, preserving enabled flag. */
|
|
288
|
+
export function applyParsedDocumentToProfile(profile, parsed) {
|
|
289
|
+
return {
|
|
290
|
+
...profile,
|
|
291
|
+
...parsed.fields,
|
|
292
|
+
document: parsed.document,
|
|
293
|
+
version: parsed.meta.version,
|
|
294
|
+
status: parsed.meta.status,
|
|
295
|
+
generatedFrom: parsed.meta.generatedFrom.length
|
|
296
|
+
? parsed.meta.generatedFrom
|
|
297
|
+
: profile.generatedFrom,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
/** Build a profile document from quick-edit fields when none is stored yet. */
|
|
301
|
+
export function ensureBrandVoiceDocument(profile) {
|
|
302
|
+
if (profile.document?.trim())
|
|
303
|
+
return profile.document.trim();
|
|
304
|
+
return serializeBrandVoiceDocument({
|
|
305
|
+
fields: {
|
|
306
|
+
name: profile.name ?? '',
|
|
307
|
+
tone: profile.tone ?? '',
|
|
308
|
+
audience: profile.audience ?? '',
|
|
309
|
+
guidelines: profile.guidelines ?? '',
|
|
310
|
+
sampleText: profile.sampleText ?? '',
|
|
311
|
+
},
|
|
312
|
+
meta: {
|
|
313
|
+
version: profile.version ?? 1,
|
|
314
|
+
status: profile.status ?? 'draft',
|
|
315
|
+
generatedFrom: profile.generatedFrom ?? [],
|
|
316
|
+
},
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
/** Sync flat fields from the stored document (when present). */
|
|
320
|
+
export function syncFieldsFromDocument(profile) {
|
|
321
|
+
if (!profile.document?.trim())
|
|
322
|
+
return profile;
|
|
323
|
+
const parsed = parseBrandVoiceDocument(profile.document);
|
|
324
|
+
if (!parsed.ok)
|
|
325
|
+
return profile;
|
|
326
|
+
return {
|
|
327
|
+
...profile,
|
|
328
|
+
...parsed.value.fields,
|
|
329
|
+
version: parsed.value.meta.version,
|
|
330
|
+
status: parsed.value.meta.status,
|
|
331
|
+
generatedFrom: parsed.value.meta.generatedFrom,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
export const BRAND_VOICE_EXPORT_FILENAME = 'brand-voice-profile.md';
|
|
335
|
+
//# sourceMappingURL=brand-voice-document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-voice-document.js","sourceRoot":"","sources":["../../src/ai/brand-voice-document.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid brand-voice fixer — deterministic rule fixes first, optional AI rewrite
|
|
3
|
+
* for remaining alignment issues.
|
|
4
|
+
*/
|
|
5
|
+
import { type BrandAlignmentIssue, type BrandAlignmentScoreResult, type BrandVoiceStructuredProfile } from './brand-voice-structured.js';
|
|
6
|
+
export interface DeterministicFixResult {
|
|
7
|
+
text: string;
|
|
8
|
+
fixes: string[];
|
|
9
|
+
}
|
|
10
|
+
/** Apply safe, deterministic fixes from structured rules (no LLM). */
|
|
11
|
+
export declare function applyDeterministicBrandFixes(content: string, structured: BrandVoiceStructuredProfile, channelId?: string): DeterministicFixResult;
|
|
12
|
+
export declare function buildBrandVoiceFixPrompt(content: string, issues: BrandAlignmentIssue[], structured: BrandVoiceStructuredProfile): {
|
|
13
|
+
system: string;
|
|
14
|
+
prompt: string;
|
|
15
|
+
};
|
|
16
|
+
export interface BrandVoiceFixOutcome {
|
|
17
|
+
text: string;
|
|
18
|
+
deterministicFixes: string[];
|
|
19
|
+
beforeScore: BrandAlignmentScoreResult;
|
|
20
|
+
afterScore: BrandAlignmentScoreResult;
|
|
21
|
+
usedAi: boolean;
|
|
22
|
+
}
|
|
23
|
+
export declare function needsAiBrandFix(score: BrandAlignmentScoreResult): boolean;
|
|
24
|
+
/** Score before/after deterministic fixes; caller runs AI when `needsAiBrandFix`. */
|
|
25
|
+
export declare function runDeterministicBrandFix(content: string, structured: BrandVoiceStructuredProfile, channel?: string): BrandVoiceFixOutcome;
|
|
26
|
+
//# sourceMappingURL=brand-voice-fix.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-voice-fix.d.ts","sourceRoot":"","sources":["../../src/ai/brand-voice-fix.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAEL,KAAK,mBAAmB,EACxB,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EACjC,MAAM,6BAA6B,CAAA;AAIpC,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,EAAE,CAAA;CAChB;AAED,sEAAsE;AACtE,wBAAgB,4BAA4B,CAC1C,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,2BAA2B,EACvC,SAAS,CAAC,EAAE,MAAM,GACjB,sBAAsB,CA2CxB;AAMD,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,mBAAmB,EAAE,EAC7B,UAAU,EAAE,2BAA2B,GACtC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CA8BpC;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAA;IACZ,kBAAkB,EAAE,MAAM,EAAE,CAAA;IAC5B,WAAW,EAAE,yBAAyB,CAAA;IACtC,UAAU,EAAE,yBAAyB,CAAA;IACrC,MAAM,EAAE,OAAO,CAAA;CAChB;AAED,wBAAgB,eAAe,CAAC,KAAK,EAAE,yBAAyB,GAAG,OAAO,CAGzE;AAED,qFAAqF;AACrF,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,2BAA2B,EACvC,OAAO,CAAC,EAAE,MAAM,GACf,oBAAoB,CAWtB"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hybrid brand-voice fixer — deterministic rule fixes first, optional AI rewrite
|
|
3
|
+
* for remaining alignment issues.
|
|
4
|
+
*/
|
|
5
|
+
import { scoreBrandAlignment, } from './brand-voice-structured.js';
|
|
6
|
+
const EMOJI_RE = /[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}]/gu;
|
|
7
|
+
/** Apply safe, deterministic fixes from structured rules (no LLM). */
|
|
8
|
+
export function applyDeterministicBrandFixes(content, structured, channelId) {
|
|
9
|
+
let text = content;
|
|
10
|
+
const fixes = [];
|
|
11
|
+
for (const banned of structured.bannedTerms) {
|
|
12
|
+
const term = banned.term.trim();
|
|
13
|
+
if (!term)
|
|
14
|
+
continue;
|
|
15
|
+
const re = new RegExp(`\\b${escapeRegExp(term)}\\b`, 'gi');
|
|
16
|
+
if (!re.test(text))
|
|
17
|
+
continue;
|
|
18
|
+
const replacement = banned.useInstead.trim() || '';
|
|
19
|
+
text = text.replace(re, (match) => {
|
|
20
|
+
if (!replacement)
|
|
21
|
+
return '';
|
|
22
|
+
if (match === match.toUpperCase())
|
|
23
|
+
return replacement.toUpperCase();
|
|
24
|
+
if (match[0] === match[0]?.toUpperCase()) {
|
|
25
|
+
return replacement.charAt(0).toUpperCase() + replacement.slice(1);
|
|
26
|
+
}
|
|
27
|
+
return replacement;
|
|
28
|
+
});
|
|
29
|
+
fixes.push(replacement
|
|
30
|
+
? `Replaced banned term "${term}" with "${replacement}".`
|
|
31
|
+
: `Removed banned term "${term}".`);
|
|
32
|
+
}
|
|
33
|
+
if (structured.mechanics.exclamationPoints === 'forbid' && text.includes('!')) {
|
|
34
|
+
text = text.replace(/!+/g, '.');
|
|
35
|
+
fixes.push('Removed exclamation points.');
|
|
36
|
+
}
|
|
37
|
+
if (structured.mechanics.emoji === 'forbid' && EMOJI_RE.test(text)) {
|
|
38
|
+
text = text.replace(EMOJI_RE, '').replace(/\s{2,}/g, ' ');
|
|
39
|
+
fixes.push('Removed emoji.');
|
|
40
|
+
}
|
|
41
|
+
const channelRule = channelId ? structured.channels.find((c) => c.id === channelId) : undefined;
|
|
42
|
+
const limit = channelRule?.maxChars ?? channelRule?.maxDescriptionChars;
|
|
43
|
+
if (typeof limit === 'number' && text.length > limit) {
|
|
44
|
+
text = text.slice(0, limit).trimEnd();
|
|
45
|
+
fixes.push(`Trimmed to ${limit} characters for ${channelRule.label}.`);
|
|
46
|
+
}
|
|
47
|
+
return { text: text.trim(), fixes };
|
|
48
|
+
}
|
|
49
|
+
function escapeRegExp(value) {
|
|
50
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
51
|
+
}
|
|
52
|
+
export function buildBrandVoiceFixPrompt(content, issues, structured) {
|
|
53
|
+
const issueLines = issues
|
|
54
|
+
.slice(0, 12)
|
|
55
|
+
.map((i) => `- ${i.message}${i.suggestion ? ` → ${i.suggestion}` : ''}`)
|
|
56
|
+
.join('\n');
|
|
57
|
+
const preferred = structured.preferredTerms.length > 0
|
|
58
|
+
? `Preferred terms: ${structured.preferredTerms.slice(0, 12).join(', ')}`
|
|
59
|
+
: '';
|
|
60
|
+
return {
|
|
61
|
+
system: 'You are a brand voice editor. Rewrite the supplied copy so it matches the brand ' +
|
|
62
|
+
'voice rules. Preserve the original meaning and approximate length unless a channel ' +
|
|
63
|
+
'character limit requires shortening. Return only the revised copy — no quotes, ' +
|
|
64
|
+
'no preamble, no markdown.',
|
|
65
|
+
prompt: [
|
|
66
|
+
'Fix this copy to resolve the alignment issues below.',
|
|
67
|
+
preferred,
|
|
68
|
+
'',
|
|
69
|
+
'Issues:',
|
|
70
|
+
issueLines || '- General off-brand tone or vocabulary.',
|
|
71
|
+
'',
|
|
72
|
+
'Copy to fix:',
|
|
73
|
+
`<<<COPY\n${content.slice(0, 4000)}\n>>>`,
|
|
74
|
+
]
|
|
75
|
+
.filter(Boolean)
|
|
76
|
+
.join('\n'),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
export function needsAiBrandFix(score) {
|
|
80
|
+
if (score.score >= 90)
|
|
81
|
+
return false;
|
|
82
|
+
return score.issues.some((i) => i.severity === 'error' || i.severity === 'warning');
|
|
83
|
+
}
|
|
84
|
+
/** Score before/after deterministic fixes; caller runs AI when `needsAiBrandFix`. */
|
|
85
|
+
export function runDeterministicBrandFix(content, structured, channel) {
|
|
86
|
+
const beforeScore = scoreBrandAlignment(content, structured, channel);
|
|
87
|
+
const { text, fixes } = applyDeterministicBrandFixes(content, structured, channel);
|
|
88
|
+
const afterScore = scoreBrandAlignment(text, structured, channel);
|
|
89
|
+
return {
|
|
90
|
+
text,
|
|
91
|
+
deterministicFixes: fixes,
|
|
92
|
+
beforeScore,
|
|
93
|
+
afterScore,
|
|
94
|
+
usedAi: false,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=brand-voice-fix.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-voice-fix.js","sourceRoot":"","sources":["../../src/ai/brand-voice-fix.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BrandVoiceProfile } from './types.js';
|
|
2
|
+
export interface PrepareBrandVoiceOptions {
|
|
3
|
+
/** When true, trust the markdown document over quick-edit fields. */
|
|
4
|
+
preferDocument?: boolean;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Normalize a brand voice payload for persistence. Quick-edit fields and the
|
|
8
|
+
* markdown document stay in sync — whichever surface the editor used last wins.
|
|
9
|
+
*/
|
|
10
|
+
export declare function prepareBrandVoiceForSave(input: Partial<BrandVoiceProfile> & PrepareBrandVoiceOptions): BrandVoiceProfile;
|
|
11
|
+
//# sourceMappingURL=brand-voice-save.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-voice-save.d.ts","sourceRoot":"","sources":["../../src/ai/brand-voice-save.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD,MAAM,WAAW,wBAAwB;IACvC,qEAAqE;IACrE,cAAc,CAAC,EAAE,OAAO,CAAA;CACzB;AAiBD;;;GAGG;AACH,wBAAgB,wBAAwB,CACtC,KAAK,EAAE,OAAO,CAAC,iBAAiB,CAAC,GAAG,wBAAwB,GAC3D,iBAAiB,CAiCnB"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { normalizeBrandVoice } from './brand-voice.js';
|
|
2
|
+
import { applyParsedDocumentToProfile, parseBrandVoiceDocument, serializeBrandVoiceDocument, } from './brand-voice-document.js';
|
|
3
|
+
import { parseStructuredProfile } from './brand-voice-structured.js';
|
|
4
|
+
function metaFromProfile(profile) {
|
|
5
|
+
return {
|
|
6
|
+
version: profile.version ?? 1,
|
|
7
|
+
status: profile.status ?? 'draft',
|
|
8
|
+
generatedFrom: profile.generatedFrom ?? [],
|
|
9
|
+
lastUpdated: new Date().toISOString().slice(0, 10),
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function fromParsed(enabled, parsed) {
|
|
13
|
+
return normalizeBrandVoice(applyParsedDocumentToProfile({ enabled }, parsed));
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Normalize a brand voice payload for persistence. Quick-edit fields and the
|
|
17
|
+
* markdown document stay in sync — whichever surface the editor used last wins.
|
|
18
|
+
*/
|
|
19
|
+
export function prepareBrandVoiceForSave(input) {
|
|
20
|
+
const enabled = Boolean(input.enabled);
|
|
21
|
+
const fields = {
|
|
22
|
+
name: input.name ?? '',
|
|
23
|
+
tone: input.tone ?? '',
|
|
24
|
+
audience: input.audience ?? '',
|
|
25
|
+
guidelines: input.guidelines ?? '',
|
|
26
|
+
sampleText: input.sampleText ?? '',
|
|
27
|
+
};
|
|
28
|
+
if (input.preferDocument && input.document?.trim()) {
|
|
29
|
+
const parsed = parseBrandVoiceDocument(input.document);
|
|
30
|
+
if (!parsed.ok)
|
|
31
|
+
throw new Error(parsed.error);
|
|
32
|
+
return fromParsed(enabled, parsed.value);
|
|
33
|
+
}
|
|
34
|
+
const meta = metaFromProfile(input);
|
|
35
|
+
const structured = input.document?.trim() ? parseStructuredProfile(input.document) : undefined;
|
|
36
|
+
const document = serializeBrandVoiceDocument({
|
|
37
|
+
fields,
|
|
38
|
+
meta: { ...meta, version: (input.version ?? 1) + 1 },
|
|
39
|
+
existingDocument: input.document,
|
|
40
|
+
structured,
|
|
41
|
+
});
|
|
42
|
+
return normalizeBrandVoice({
|
|
43
|
+
enabled,
|
|
44
|
+
...fields,
|
|
45
|
+
document,
|
|
46
|
+
version: (input.version ?? 1) + 1,
|
|
47
|
+
status: meta.status,
|
|
48
|
+
generatedFrom: meta.generatedFrom.length ? meta.generatedFrom : undefined,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=brand-voice-save.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-voice-save.js","sourceRoot":"","sources":["../../src/ai/brand-voice-save.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAA;AACtD,OAAO,EACL,4BAA4B,EAC5B,uBAAuB,EACvB,2BAA2B,GAE5B,MAAM,2BAA2B,CAAA;AAClC,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAA;AAQpE,SAAS,eAAe,CAAC,OAAmC;IAC1D,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,CAAC;QAC7B,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,OAAO;QACjC,aAAa,EAAE,OAAO,CAAC,aAAa,IAAI,EAAE;QAC1C,WAAW,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;KAC1C,CAAA;AACZ,CAAC;AAED,SAAS,UAAU,CAAC,OAAgB,EAAE,MAAgC;IACpE,OAAO,mBAAmB,CACxB,4BAA4B,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,CAA+B,CAChF,CAAA;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CACtC,KAA4D;IAE5D,MAAM,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAA;IACtC,MAAM,MAAM,GAAG;QACb,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,EAAE;QACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,EAAE;QAC9B,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,EAAE;QAClC,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,EAAE;KACnC,CAAA;IAED,IAAI,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,uBAAuB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAA;QACtD,IAAI,CAAC,MAAM,CAAC,EAAE;YAAE,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;QAC7C,OAAO,UAAU,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAA;IAC1C,CAAC;IAED,MAAM,IAAI,GAAG,eAAe,CAAC,KAAK,CAAC,CAAA;IACnC,MAAM,UAAU,GAAG,KAAK,CAAC,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,sBAAsB,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IAC9F,MAAM,QAAQ,GAAG,2BAA2B,CAAC;QAC3C,MAAM;QACN,IAAI,EAAE,EAAE,GAAG,IAAI,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE;QACpD,gBAAgB,EAAE,KAAK,CAAC,QAAQ;QAChC,UAAU;KACX,CAAC,CAAA;IAEF,OAAO,mBAAmB,CAAC;QACzB,OAAO;QACP,GAAG,MAAM;QACT,QAAQ;QACR,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,IAAI,CAAC,CAAC,GAAG,CAAC;QACjC,MAAM,EAAE,IAAI,CAAC,MAAM;QACnB,aAAa,EAAE,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,SAAS;KAC1E,CAAC,CAAA;AACJ,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* External sources for brand voice analysis — SSRF-safe URL fetch and pasted uploads.
|
|
3
|
+
*/
|
|
4
|
+
import type { BrandVoiceSample } from './brand-voice-analyze.js';
|
|
5
|
+
export declare const MAX_ANALYZE_URLS = 5;
|
|
6
|
+
export declare const MAX_ANALYZE_UPLOADS = 5;
|
|
7
|
+
export declare const MAX_UPLOAD_TEXT_CHARS = 15000;
|
|
8
|
+
export declare const MAX_URL_FETCH_CHARS = 8000;
|
|
9
|
+
export declare const MAX_URL_FETCH_BYTES: number;
|
|
10
|
+
export declare class BrandVoiceSourceError extends Error {
|
|
11
|
+
constructor(message: string);
|
|
12
|
+
}
|
|
13
|
+
/** Fetch and extract readable text from a public URL (SSRF-safe, no redirects). */
|
|
14
|
+
export declare function fetchBrandVoiceUrlSample(url: string): Promise<BrandVoiceSample>;
|
|
15
|
+
export declare function uploadToBrandVoiceSample(name: string, text: string): BrandVoiceSample;
|
|
16
|
+
//# sourceMappingURL=brand-voice-sources.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brand-voice-sources.d.ts","sourceRoot":"","sources":["../../src/ai/brand-voice-sources.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAA;AAEhE,eAAO,MAAM,gBAAgB,IAAI,CAAA;AACjC,eAAO,MAAM,mBAAmB,IAAI,CAAA;AACpC,eAAO,MAAM,qBAAqB,QAAS,CAAA;AAC3C,eAAO,MAAM,mBAAmB,OAAQ,CAAA;AACxC,eAAO,MAAM,mBAAmB,QAAa,CAAA;AAqB7C,qBAAa,qBAAsB,SAAQ,KAAK;gBAClC,OAAO,EAAE,MAAM;CAI5B;AAED,mFAAmF;AACnF,wBAAsB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA4DrF;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,gBAAgB,CAgBrF"}
|