@consilioweb/payload-seo-analyzer 1.9.0 → 1.11.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/dist/client.js CHANGED
@@ -1059,6 +1059,9 @@ var fr = {
1059
1059
  pagesAnalyzed: "pages analys\xE9es",
1060
1060
  markCornerstone: "Marquer pilier",
1061
1061
  unmarkCornerstone: "D\xE9marquer pilier",
1062
+ bulkOptimizeMeta: "Optimiser m\xE9ta (IA)",
1063
+ bulkOptimizing: "Optimisation\u2026",
1064
+ bulkConfirm: "Confirmer ?",
1062
1065
  searchPlaceholder: "Rechercher (titre, slug, keyword)...",
1063
1066
  allCollections: "Toutes les collections",
1064
1067
  allScores: "Tous les scores",
@@ -1651,6 +1654,9 @@ var en = {
1651
1654
  pagesAnalyzed: "pages analyzed",
1652
1655
  markCornerstone: "Mark as cornerstone",
1653
1656
  unmarkCornerstone: "Unmark cornerstone",
1657
+ bulkOptimizeMeta: "Optimize meta (AI)",
1658
+ bulkOptimizing: "Optimizing\u2026",
1659
+ bulkConfirm: "Confirm?",
1654
1660
  searchPlaceholder: "Search (title, slug, keyword)...",
1655
1661
  allCollections: "All collections",
1656
1662
  allScores: "All scores",
@@ -9195,8 +9201,11 @@ function BulkActionBar({
9195
9201
  onExportCsv,
9196
9202
  onMarkCornerstone,
9197
9203
  onUnmarkCornerstone,
9204
+ onOptimizeMeta,
9205
+ optimizing,
9198
9206
  t
9199
9207
  }) {
9208
+ const [confirmOptimize, setConfirmOptimize] = useState(false);
9200
9209
  if (count === 0) return null;
9201
9210
  return /* @__PURE__ */ jsxs(
9202
9211
  "div",
@@ -9242,6 +9251,24 @@ function BulkActionBar({
9242
9251
  children: t.common.exportCsv
9243
9252
  }
9244
9253
  ),
9254
+ /* @__PURE__ */ jsx(
9255
+ "button",
9256
+ {
9257
+ onClick: () => {
9258
+ if (optimizing) return;
9259
+ if (confirmOptimize) {
9260
+ setConfirmOptimize(false);
9261
+ onOptimizeMeta();
9262
+ } else {
9263
+ setConfirmOptimize(true);
9264
+ setTimeout(() => setConfirmOptimize(false), 4e3);
9265
+ }
9266
+ },
9267
+ disabled: optimizing,
9268
+ style: { ...btnBase, backgroundColor: "#7c3aed", color: "#fff", opacity: optimizing ? 0.6 : 1 },
9269
+ children: optimizing ? t.seoView.bulkOptimizing : confirmOptimize ? t.seoView.bulkConfirm : `\u2728 ${t.seoView.bulkOptimizeMeta}`
9270
+ }
9271
+ ),
9245
9272
  /* @__PURE__ */ jsx(
9246
9273
  "button",
9247
9274
  {
@@ -9282,6 +9309,7 @@ function SeoView() {
9282
9309
  const [expandedId, setExpandedId] = useState(null);
9283
9310
  const [saving, setSaving] = useState(false);
9284
9311
  const [saveError, setSaveError] = useState(null);
9312
+ const [bulkOptimizing, setBulkOptimizing] = useState(false);
9285
9313
  const PAGE_SIZE = 50;
9286
9314
  const fetchAudit = useCallback(async (forceRefresh = false) => {
9287
9315
  setLoading(true);
@@ -9502,6 +9530,45 @@ function SeoView() {
9502
9530
  },
9503
9531
  [selectedIds, fetchAudit]
9504
9532
  );
9533
+ const handleBulkOptimizeMeta = useCallback(async () => {
9534
+ const keys = Array.from(selectedIds);
9535
+ if (keys.length === 0) return;
9536
+ setBulkOptimizing(true);
9537
+ for (const key of keys) {
9538
+ const [collection, id] = key.split("::");
9539
+ if (!collection || !id || collection.startsWith("global:")) continue;
9540
+ try {
9541
+ const res = await fetch("/api/seo-plugin/ai-optimize", {
9542
+ method: "POST",
9543
+ headers: { "Content-Type": "application/json" },
9544
+ credentials: "include",
9545
+ body: JSON.stringify({ collection, id })
9546
+ });
9547
+ if (!res.ok) continue;
9548
+ const data = await res.json();
9549
+ const sug = data.suggestions || {};
9550
+ const patch = {};
9551
+ if (sug.metaTitle || sug.metaDescription) {
9552
+ patch.meta = { title: sug.metaTitle, description: sug.metaDescription };
9553
+ }
9554
+ if (sug.focusKeyword && sug.focusKeyword !== data.current?.focusKeyword) {
9555
+ patch.focusKeyword = sug.focusKeyword;
9556
+ }
9557
+ if (Object.keys(patch).length > 0) {
9558
+ await fetch(`/api/${collection}/${id}`, {
9559
+ method: "PATCH",
9560
+ headers: { "Content-Type": "application/json" },
9561
+ credentials: "include",
9562
+ body: JSON.stringify(patch)
9563
+ });
9564
+ }
9565
+ } catch {
9566
+ }
9567
+ }
9568
+ setBulkOptimizing(false);
9569
+ setSelectedIds(/* @__PURE__ */ new Set());
9570
+ fetchAudit();
9571
+ }, [selectedIds, fetchAudit]);
9505
9572
  const handleInlineSave = useCallback(
9506
9573
  async (item, metaTitle, metaDescription) => {
9507
9574
  setSaving(true);
@@ -10250,6 +10317,8 @@ function SeoView() {
10250
10317
  onExportCsv: handleBulkExportCsv,
10251
10318
  onMarkCornerstone: () => handleBulkCornerstone(true),
10252
10319
  onUnmarkCornerstone: () => handleBulkCornerstone(false),
10320
+ onOptimizeMeta: handleBulkOptimizeMeta,
10321
+ optimizing: bulkOptimizing,
10253
10322
  t
10254
10323
  }
10255
10324
  )
@@ -15904,7 +15973,7 @@ function getSchemaTypes(t) {
15904
15973
  }
15905
15974
  };
15906
15975
  }
15907
- function buildJsonLd(type, values) {
15976
+ function buildJsonLd2(type, values) {
15908
15977
  switch (type) {
15909
15978
  case "LocalBusiness": {
15910
15979
  const result = {
@@ -16185,7 +16254,7 @@ function SchemaBuilderView() {
16185
16254
  [arrayName]: (prev[arrayName] || []).filter((_, i) => i !== index)
16186
16255
  }));
16187
16256
  }, []);
16188
- const jsonLd = useMemo(() => buildJsonLd(selectedType, values), [selectedType, values]);
16257
+ const jsonLd = useMemo(() => buildJsonLd2(selectedType, values), [selectedType, values]);
16189
16258
  const jsonString = useMemo(() => JSON.stringify(jsonLd, null, 2), [jsonLd]);
16190
16259
  const scriptTag = useMemo(
16191
16260
  () => `<script type="application/ld+json">
@@ -16962,6 +17031,573 @@ function GscPanel({ locale }) {
16962
17031
  ] })
16963
17032
  ] });
16964
17033
  }
17034
+ var C6 = {
17035
+ text: "var(--theme-text, #1a1a1a)",
17036
+ sub: "var(--theme-elevation-600, #6b7280)",
17037
+ card: "var(--theme-elevation-50, #f9fafb)",
17038
+ border: "var(--theme-elevation-200, #e5e7eb)",
17039
+ green: "#22c55e",
17040
+ red: "#ef4444",
17041
+ blue: "#3b82f6"
17042
+ };
17043
+ var S3 = {
17044
+ fr: {
17045
+ title: "Suivi de positions (rank tracking)",
17046
+ subtitle: "Historique quotidien des positions Google (via Search Console) et mouvements dans le temps.",
17047
+ needGsc: "Connectez Google Search Console ci-dessus pour activer le suivi de positions.",
17048
+ snapshot: "Relever maintenant",
17049
+ snapshotting: "Relev\xE9 en cours\u2026",
17050
+ noData: "Pas encore de donn\xE9es. Le relev\xE9 tourne automatiquement chaque jour ; cliquez \xAB Relever maintenant \xBB pour d\xE9marrer.",
17051
+ lastSnapshot: "Dernier relev\xE9",
17052
+ query: "Requ\xEAte",
17053
+ position: "Position",
17054
+ change: "\xC9volution",
17055
+ clicks: "Clics",
17056
+ impressions: "Impr.",
17057
+ stable: "stable",
17058
+ newQ: "nouveau",
17059
+ countLabel: "requ\xEAtes suivies"
17060
+ },
17061
+ en: {
17062
+ title: "Rank tracking",
17063
+ subtitle: "Daily Google position history (via Search Console) and movement over time.",
17064
+ needGsc: "Connect Google Search Console above to enable rank tracking.",
17065
+ snapshot: "Snapshot now",
17066
+ snapshotting: "Snapshotting\u2026",
17067
+ noData: 'No data yet. The snapshot runs automatically every day; click "Snapshot now" to start.',
17068
+ lastSnapshot: "Last snapshot",
17069
+ query: "Query",
17070
+ position: "Position",
17071
+ change: "Change",
17072
+ clicks: "Clicks",
17073
+ impressions: "Impr.",
17074
+ stable: "stable",
17075
+ newQ: "new",
17076
+ countLabel: "tracked queries"
17077
+ }
17078
+ };
17079
+ function RankTrackingPanel({ locale }) {
17080
+ const s = S3[locale] ?? S3.fr;
17081
+ const [movers, setMovers] = useState(null);
17082
+ const [lastSnapshot, setLastSnapshot] = useState(null);
17083
+ const [loading, setLoading] = useState(true);
17084
+ const [busy, setBusy] = useState(false);
17085
+ const [error, setError] = useState(null);
17086
+ const [notConnected, setNotConnected] = useState(false);
17087
+ const load = useCallback(async () => {
17088
+ setLoading(true);
17089
+ setError(null);
17090
+ try {
17091
+ const res = await fetch("/api/seo-plugin/rank-history", { credentials: "include", cache: "no-store" });
17092
+ if (res.status === 403 || res.status === 409) {
17093
+ setNotConnected(true);
17094
+ setMovers(null);
17095
+ return;
17096
+ }
17097
+ const json = await res.json();
17098
+ if (!res.ok) {
17099
+ setError(json.error || `Error ${res.status}`);
17100
+ return;
17101
+ }
17102
+ setMovers(json.movers || []);
17103
+ setLastSnapshot(json.lastSnapshot || null);
17104
+ } catch (e) {
17105
+ setError(e instanceof Error ? e.message : "Network error");
17106
+ } finally {
17107
+ setLoading(false);
17108
+ }
17109
+ }, []);
17110
+ useEffect(() => {
17111
+ void load();
17112
+ }, [load]);
17113
+ const snapshotNow = async () => {
17114
+ setBusy(true);
17115
+ setError(null);
17116
+ try {
17117
+ const res = await fetch("/api/seo-plugin/rank-snapshot", { method: "POST", credentials: "include" });
17118
+ const json = await res.json();
17119
+ if (res.status === 409) {
17120
+ setNotConnected(true);
17121
+ return;
17122
+ }
17123
+ if (!res.ok) {
17124
+ setError(json.error || json.reason || `Error ${res.status}`);
17125
+ return;
17126
+ }
17127
+ await load();
17128
+ } catch (e) {
17129
+ setError(e instanceof Error ? e.message : "Network error");
17130
+ } finally {
17131
+ setBusy(false);
17132
+ }
17133
+ };
17134
+ const card = {
17135
+ padding: 16,
17136
+ borderRadius: 12,
17137
+ border: `1px solid ${C6.border}`,
17138
+ backgroundColor: C6.card,
17139
+ marginBottom: 20
17140
+ };
17141
+ const btn = {
17142
+ padding: "8px 12px",
17143
+ borderRadius: 8,
17144
+ border: `1px solid ${C6.blue}`,
17145
+ backgroundColor: C6.blue,
17146
+ color: "#fff",
17147
+ fontSize: 12,
17148
+ fontWeight: 700,
17149
+ cursor: busy ? "wait" : "pointer",
17150
+ opacity: busy ? 0.6 : 1
17151
+ };
17152
+ const renderDelta = (m) => {
17153
+ if (m.previousPosition === null) {
17154
+ return /* @__PURE__ */ jsx("span", { style: { color: C6.sub, fontSize: 11 }, children: s.newQ });
17155
+ }
17156
+ if (Math.abs(m.delta) < 0.1) {
17157
+ return /* @__PURE__ */ jsxs("span", { style: { color: C6.sub, fontSize: 11 }, children: [
17158
+ "\u2014 ",
17159
+ s.stable
17160
+ ] });
17161
+ }
17162
+ const up = m.delta > 0;
17163
+ return /* @__PURE__ */ jsxs("span", { style: { color: up ? C6.green : C6.red, fontWeight: 700, fontSize: 12 }, children: [
17164
+ up ? "\u25B2" : "\u25BC",
17165
+ " ",
17166
+ Math.abs(m.delta).toFixed(1)
17167
+ ] });
17168
+ };
17169
+ return /* @__PURE__ */ jsxs("div", { style: card, children: [
17170
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, flexWrap: "wrap" }, children: [
17171
+ /* @__PURE__ */ jsxs("div", { children: [
17172
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 16, fontWeight: 800, color: C6.text }, children: s.title }),
17173
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: C6.sub, marginTop: 2 }, children: s.subtitle })
17174
+ ] }),
17175
+ !notConnected && /* @__PURE__ */ jsx("button", { type: "button", onClick: snapshotNow, disabled: busy, style: btn, children: busy ? s.snapshotting : s.snapshot })
17176
+ ] }),
17177
+ error && /* @__PURE__ */ jsx("div", { style: { color: C6.red, fontSize: 13, fontWeight: 600, marginTop: 10 }, children: error }),
17178
+ notConnected && /* @__PURE__ */ jsx("div", { style: { marginTop: 12, fontSize: 13, color: C6.sub }, children: s.needGsc }),
17179
+ !notConnected && /* @__PURE__ */ jsxs("div", { style: { marginTop: 12 }, children: [
17180
+ lastSnapshot && /* @__PURE__ */ jsxs("div", { style: { fontSize: 11, color: C6.sub, marginBottom: 8 }, children: [
17181
+ s.lastSnapshot,
17182
+ ": ",
17183
+ new Date(lastSnapshot).toLocaleString(locale),
17184
+ movers ? ` \xB7 ${movers.length} ${s.countLabel}` : ""
17185
+ ] }),
17186
+ loading && !movers && /* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: C6.sub }, children: "\u2026" }),
17187
+ movers && movers.length === 0 && /* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: C6.sub }, children: s.noData }),
17188
+ movers && movers.length > 0 && /* @__PURE__ */ jsx("div", { style: { overflowX: "auto" }, children: /* @__PURE__ */ jsxs("table", { style: { width: "100%", borderCollapse: "collapse", fontSize: 12 }, children: [
17189
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { style: { textAlign: "left", color: C6.sub }, children: [
17190
+ /* @__PURE__ */ jsx("th", { style: { padding: "6px 8px" }, children: s.query }),
17191
+ /* @__PURE__ */ jsx("th", { style: { padding: "6px 8px", textAlign: "right" }, children: s.position }),
17192
+ /* @__PURE__ */ jsx("th", { style: { padding: "6px 8px", textAlign: "right" }, children: s.change }),
17193
+ /* @__PURE__ */ jsx("th", { style: { padding: "6px 8px", textAlign: "right" }, children: s.clicks }),
17194
+ /* @__PURE__ */ jsx("th", { style: { padding: "6px 8px", textAlign: "right" }, children: s.impressions })
17195
+ ] }) }),
17196
+ /* @__PURE__ */ jsx("tbody", { children: movers.slice(0, 100).map((m, i) => /* @__PURE__ */ jsxs("tr", { style: { borderTop: `1px solid ${C6.border}`, color: C6.text }, children: [
17197
+ /* @__PURE__ */ jsx("td", { style: { padding: "6px 8px", maxWidth: 320, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: m.query }),
17198
+ /* @__PURE__ */ jsx("td", { style: { padding: "6px 8px", textAlign: "right", fontWeight: 700 }, children: m.position.toFixed(1) }),
17199
+ /* @__PURE__ */ jsx("td", { style: { padding: "6px 8px", textAlign: "right" }, children: renderDelta(m) }),
17200
+ /* @__PURE__ */ jsx("td", { style: { padding: "6px 8px", textAlign: "right" }, children: m.clicks }),
17201
+ /* @__PURE__ */ jsx("td", { style: { padding: "6px 8px", textAlign: "right" }, children: m.impressions })
17202
+ ] }, i)) })
17203
+ ] }) })
17204
+ ] })
17205
+ ] });
17206
+ }
17207
+ var C7 = {
17208
+ text: "var(--theme-text, #1a1a1a)",
17209
+ sub: "var(--theme-elevation-600, #6b7280)",
17210
+ card: "var(--theme-elevation-50, #f9fafb)",
17211
+ border: "var(--theme-elevation-200, #e5e7eb)",
17212
+ green: "#22c55e",
17213
+ red: "#ef4444",
17214
+ amber: "#f59e0b",
17215
+ blue: "#3b82f6"
17216
+ };
17217
+ var S4 = {
17218
+ fr: {
17219
+ title: "Monitoring & alertes",
17220
+ subtitle: "Digest p\xE9riodique : r\xE9gressions de score, nouveaux 404, chutes de position (webhook / email).",
17221
+ notConfigured: "Aucun canal configur\xE9. D\xE9finissez SEO_ALERT_WEBHOOK_URL et/ou SEO_ALERT_EMAIL c\xF4t\xE9 serveur, puis activez features.alerts.",
17222
+ webhook: "Webhook",
17223
+ email: "Email",
17224
+ configured: "configur\xE9",
17225
+ missing: "absent",
17226
+ preview: "Aper\xE7u",
17227
+ sendNow: "Envoyer maintenant",
17228
+ sending: "Envoi\u2026",
17229
+ loading: "Chargement\u2026",
17230
+ noIssues: "Aucun probl\xE8me d\xE9tect\xE9 sur la p\xE9riode. \u{1F389}",
17231
+ scoreReg: "R\xE9gressions de score",
17232
+ notFound: "Nouveaux 404",
17233
+ rankDrops: "Chutes de position",
17234
+ sent: "Digest envoy\xE9",
17235
+ nothingToSend: "Rien \xE0 envoyer (aucun probl\xE8me).",
17236
+ issues: "probl\xE8me(s)"
17237
+ },
17238
+ en: {
17239
+ title: "Monitoring & alerts",
17240
+ subtitle: "Periodic digest: score regressions, new 404s, ranking drops (webhook / email).",
17241
+ notConfigured: "No channel configured. Set SEO_ALERT_WEBHOOK_URL and/or SEO_ALERT_EMAIL on the server, then enable features.alerts.",
17242
+ webhook: "Webhook",
17243
+ email: "Email",
17244
+ configured: "configured",
17245
+ missing: "missing",
17246
+ preview: "Preview",
17247
+ sendNow: "Send now",
17248
+ sending: "Sending\u2026",
17249
+ loading: "Loading\u2026",
17250
+ noIssues: "No issues for the period. \u{1F389}",
17251
+ scoreReg: "Score regressions",
17252
+ notFound: "New 404s",
17253
+ rankDrops: "Ranking drops",
17254
+ sent: "Digest sent",
17255
+ nothingToSend: "Nothing to send (no issues).",
17256
+ issues: "issue(s)"
17257
+ }
17258
+ };
17259
+ function AlertsPanel({ locale }) {
17260
+ const s = S4[locale] ?? S4.fr;
17261
+ const [digest, setDigest] = useState(null);
17262
+ const [config, setConfig] = useState(null);
17263
+ const [loading, setLoading] = useState(true);
17264
+ const [busy, setBusy] = useState(false);
17265
+ const [error, setError] = useState(null);
17266
+ const [notice, setNotice] = useState(null);
17267
+ const load = useCallback(async () => {
17268
+ setLoading(true);
17269
+ setError(null);
17270
+ try {
17271
+ const res = await fetch("/api/seo-plugin/alerts-digest", { credentials: "include", cache: "no-store" });
17272
+ if (res.status === 404 || res.status === 403) {
17273
+ setConfig({ webhookConfigured: false, emailConfigured: false, scoreDrop: 0, positionDrop: 0, windowHours: 0 });
17274
+ setDigest(null);
17275
+ return;
17276
+ }
17277
+ const json = await res.json();
17278
+ if (!res.ok) {
17279
+ setError(json.error || `Error ${res.status}`);
17280
+ return;
17281
+ }
17282
+ setDigest(json.digest);
17283
+ setConfig(json.config);
17284
+ } catch (e) {
17285
+ setError(e instanceof Error ? e.message : "Network error");
17286
+ } finally {
17287
+ setLoading(false);
17288
+ }
17289
+ }, []);
17290
+ useEffect(() => {
17291
+ void load();
17292
+ }, [load]);
17293
+ const sendNow = async () => {
17294
+ setBusy(true);
17295
+ setError(null);
17296
+ setNotice(null);
17297
+ try {
17298
+ const res = await fetch("/api/seo-plugin/alerts-run", { method: "POST", credentials: "include" });
17299
+ const json = await res.json();
17300
+ if (!res.ok) {
17301
+ setError(json.error || `Error ${res.status}`);
17302
+ return;
17303
+ }
17304
+ setNotice(json.delivery?.sent ? s.sent : s.nothingToSend);
17305
+ if (json.digest) setDigest(json.digest);
17306
+ } catch (e) {
17307
+ setError(e instanceof Error ? e.message : "Network error");
17308
+ } finally {
17309
+ setBusy(false);
17310
+ }
17311
+ };
17312
+ const card = {
17313
+ padding: 16,
17314
+ borderRadius: 12,
17315
+ border: `1px solid ${C7.border}`,
17316
+ backgroundColor: C7.card,
17317
+ marginBottom: 20
17318
+ };
17319
+ const btn = {
17320
+ padding: "8px 12px",
17321
+ borderRadius: 8,
17322
+ border: `1px solid ${C7.blue}`,
17323
+ backgroundColor: C7.blue,
17324
+ color: "#fff",
17325
+ fontSize: 12,
17326
+ fontWeight: 700,
17327
+ cursor: busy ? "wait" : "pointer",
17328
+ opacity: busy ? 0.6 : 1
17329
+ };
17330
+ const chip = (ok, label) => ({
17331
+ fontSize: 11,
17332
+ fontWeight: 700,
17333
+ padding: "3px 8px",
17334
+ borderRadius: 999,
17335
+ color: "#fff",
17336
+ backgroundColor: ok ? C7.green : C7.sub,
17337
+ marginRight: 6
17338
+ });
17339
+ const hasChannel = config && (config.webhookConfigured || config.emailConfigured);
17340
+ const list = (title, rows) => rows.length ? /* @__PURE__ */ jsxs("div", { style: { marginTop: 10 }, children: [
17341
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, fontWeight: 700, color: C7.text, marginBottom: 4 }, children: [
17342
+ title,
17343
+ " ",
17344
+ /* @__PURE__ */ jsxs("span", { style: { color: C7.amber }, children: [
17345
+ "(",
17346
+ rows.length,
17347
+ ")"
17348
+ ] })
17349
+ ] }),
17350
+ /* @__PURE__ */ jsx("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 12, color: C7.sub, lineHeight: 1.6 }, children: rows.slice(0, 8).map((r, i) => /* @__PURE__ */ jsx("li", { children: r }, i)) })
17351
+ ] }) : null;
17352
+ return /* @__PURE__ */ jsxs("div", { style: card, children: [
17353
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, flexWrap: "wrap" }, children: [
17354
+ /* @__PURE__ */ jsxs("div", { children: [
17355
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 16, fontWeight: 800, color: C7.text }, children: s.title }),
17356
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: C7.sub, marginTop: 2 }, children: s.subtitle })
17357
+ ] }),
17358
+ hasChannel && /* @__PURE__ */ jsx("button", { type: "button", onClick: sendNow, disabled: busy, style: btn, children: busy ? s.sending : s.sendNow })
17359
+ ] }),
17360
+ error && /* @__PURE__ */ jsx("div", { style: { color: C7.red, fontSize: 13, fontWeight: 600, marginTop: 10 }, children: error }),
17361
+ notice && /* @__PURE__ */ jsx("div", { style: { color: C7.green, fontSize: 13, fontWeight: 600, marginTop: 10 }, children: notice }),
17362
+ config && /* @__PURE__ */ jsxs("div", { style: { marginTop: 12 }, children: [
17363
+ /* @__PURE__ */ jsxs("span", { style: chip(config.webhookConfigured, s.webhook), children: [
17364
+ s.webhook,
17365
+ ": ",
17366
+ config.webhookConfigured ? s.configured : s.missing
17367
+ ] }),
17368
+ /* @__PURE__ */ jsxs("span", { style: chip(config.emailConfigured, s.email), children: [
17369
+ s.email,
17370
+ ": ",
17371
+ config.emailConfigured ? s.configured : s.missing
17372
+ ] })
17373
+ ] }),
17374
+ !loading && !hasChannel && /* @__PURE__ */ jsx("div", { style: { marginTop: 12, fontSize: 13, color: C7.sub }, children: s.notConfigured }),
17375
+ loading && /* @__PURE__ */ jsx("div", { style: { marginTop: 12, fontSize: 13, color: C7.sub }, children: s.loading }),
17376
+ digest && /* @__PURE__ */ jsxs("div", { style: { marginTop: 8 }, children: [
17377
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: C7.sub, marginTop: 6 }, children: [
17378
+ digest.totalIssues,
17379
+ " ",
17380
+ s.issues
17381
+ ] }),
17382
+ digest.totalIssues === 0 && /* @__PURE__ */ jsx("div", { style: { marginTop: 8, fontSize: 13, color: C7.sub }, children: s.noIssues }),
17383
+ list(
17384
+ s.scoreReg,
17385
+ digest.scoreRegressions.map((r) => `${r.collection}/${r.documentId} \u2014 ${r.from} \u2192 ${r.to} (\u2212${r.drop})`)
17386
+ ),
17387
+ list(s.notFound, digest.newNotFound.map((n) => `${n.url} \u2014 ${n.count}\xD7`)),
17388
+ list(s.rankDrops, digest.rankDrops.map((d) => `\u201C${d.query}\u201D \u2014 #${d.from} \u2192 #${d.to} (\u25BC${d.drop})`))
17389
+ ] })
17390
+ ] });
17391
+ }
17392
+ var C8 = {
17393
+ text: "var(--theme-text, #1a1a1a)",
17394
+ sub: "var(--theme-elevation-600, #6b7280)",
17395
+ card: "var(--theme-elevation-50, #f9fafb)",
17396
+ bg: "var(--theme-elevation-0, #fff)",
17397
+ border: "var(--theme-elevation-200, #e5e7eb)",
17398
+ green: "#22c55e",
17399
+ red: "#ef4444",
17400
+ violet: "#7c3aed"
17401
+ };
17402
+ var S5 = {
17403
+ fr: {
17404
+ title: "Alt-text IA des images",
17405
+ subtitle: "G\xE9n\xE8re l'attribut alt des images qui n'en ont pas (Claude vision), pour l'accessibilit\xE9 et le SEO.",
17406
+ none: "Toutes les images ont un alt. \u{1F389}",
17407
+ forbidden: "R\xE9serv\xE9 aux administrateurs.",
17408
+ disabled: "Fonction IA d\xE9sactiv\xE9e (features.aiFeatures).",
17409
+ missing: "image(s) sans alt",
17410
+ generate: "G\xE9n\xE9rer",
17411
+ generating: "\u2026",
17412
+ apply: "Appliquer",
17413
+ applied: "Appliqu\xE9 \u2713",
17414
+ noKey: "Cl\xE9 API Claude requise (ANTHROPIC_API_KEY).",
17415
+ loading: "Chargement\u2026",
17416
+ refresh: "Rafra\xEEchir"
17417
+ },
17418
+ en: {
17419
+ title: "AI image alt-text",
17420
+ subtitle: "Generate alt text for images that lack one (Claude vision), for accessibility and SEO.",
17421
+ none: "All images have alt text. \u{1F389}",
17422
+ forbidden: "Admins only.",
17423
+ disabled: "AI feature disabled (features.aiFeatures).",
17424
+ missing: "image(s) without alt",
17425
+ generate: "Generate",
17426
+ generating: "\u2026",
17427
+ apply: "Apply",
17428
+ applied: "Applied \u2713",
17429
+ noKey: "Claude API key required (ANTHROPIC_API_KEY).",
17430
+ loading: "Loading\u2026",
17431
+ refresh: "Refresh"
17432
+ }
17433
+ };
17434
+ function AltTextPanel({ locale }) {
17435
+ const s = S5[locale] ?? S5.fr;
17436
+ const [items, setItems] = useState(null);
17437
+ const [collection, setCollection] = useState("media");
17438
+ const [missingCount, setMissingCount] = useState(0);
17439
+ const [loading, setLoading] = useState(true);
17440
+ const [state, setState] = useState({});
17441
+ const [status, setStatus] = useState("ok");
17442
+ const load = useCallback(async () => {
17443
+ setLoading(true);
17444
+ try {
17445
+ const res = await fetch("/api/seo-plugin/alt-text-audit", { credentials: "include", cache: "no-store" });
17446
+ if (res.status === 404) {
17447
+ setStatus("disabled");
17448
+ return;
17449
+ }
17450
+ if (res.status === 403) {
17451
+ setStatus("forbidden");
17452
+ return;
17453
+ }
17454
+ const json = await res.json();
17455
+ setStatus("ok");
17456
+ setItems(json.items || []);
17457
+ setMissingCount(json.missingCount || 0);
17458
+ setCollection(json.collection || "media");
17459
+ } catch {
17460
+ setItems([]);
17461
+ } finally {
17462
+ setLoading(false);
17463
+ }
17464
+ }, []);
17465
+ useEffect(() => {
17466
+ void load();
17467
+ }, [load]);
17468
+ const setRow = (id, patch) => setState((prev) => ({ ...prev, [id]: { ...prev[id], ...patch } }));
17469
+ const generate = async (item) => {
17470
+ setRow(item.id, { busy: true, error: void 0 });
17471
+ try {
17472
+ const res = await fetch("/api/seo-plugin/ai-alt-text", {
17473
+ method: "POST",
17474
+ credentials: "include",
17475
+ headers: { "Content-Type": "application/json" },
17476
+ body: JSON.stringify({ collection, id: item.id, apply: false })
17477
+ });
17478
+ const json = await res.json();
17479
+ if (!res.ok) {
17480
+ setRow(item.id, { busy: false, error: json.code === "no_api_key" ? s.noKey : json.error || `Error ${res.status}` });
17481
+ return;
17482
+ }
17483
+ setRow(item.id, { busy: false, alt: json.alt });
17484
+ } catch (e) {
17485
+ setRow(item.id, { busy: false, error: e instanceof Error ? e.message : "Network error" });
17486
+ }
17487
+ };
17488
+ const apply = async (item) => {
17489
+ const alt = state[item.id]?.alt;
17490
+ if (!alt) return;
17491
+ setRow(item.id, { busy: true, error: void 0 });
17492
+ try {
17493
+ const res = await fetch("/api/seo-plugin/ai-alt-text", {
17494
+ method: "POST",
17495
+ credentials: "include",
17496
+ headers: { "Content-Type": "application/json" },
17497
+ body: JSON.stringify({ collection, id: item.id, apply: true, altText: alt })
17498
+ });
17499
+ const json = await res.json();
17500
+ if (!res.ok) {
17501
+ setRow(item.id, { busy: false, error: json.error || `Error ${res.status}` });
17502
+ return;
17503
+ }
17504
+ setRow(item.id, { busy: false, applied: true });
17505
+ } catch (e) {
17506
+ setRow(item.id, { busy: false, error: e instanceof Error ? e.message : "Network error" });
17507
+ }
17508
+ };
17509
+ const card = {
17510
+ padding: 16,
17511
+ borderRadius: 12,
17512
+ border: `1px solid ${C8.border}`,
17513
+ backgroundColor: C8.card,
17514
+ marginBottom: 20
17515
+ };
17516
+ const btn = (bg) => ({
17517
+ padding: "6px 10px",
17518
+ borderRadius: 6,
17519
+ border: `1px solid ${bg}`,
17520
+ backgroundColor: bg,
17521
+ color: "#fff",
17522
+ fontSize: 11,
17523
+ fontWeight: 700,
17524
+ cursor: "pointer"
17525
+ });
17526
+ return /* @__PURE__ */ jsxs("div", { style: card, children: [
17527
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 8, flexWrap: "wrap" }, children: [
17528
+ /* @__PURE__ */ jsxs("div", { children: [
17529
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 16, fontWeight: 800, color: C8.text }, children: s.title }),
17530
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: C8.sub, marginTop: 2 }, children: s.subtitle })
17531
+ ] }),
17532
+ status === "ok" && /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void load(), style: btn(C8.sub), children: s.refresh })
17533
+ ] }),
17534
+ status === "forbidden" && /* @__PURE__ */ jsx("div", { style: { marginTop: 12, fontSize: 13, color: C8.sub }, children: s.forbidden }),
17535
+ status === "disabled" && /* @__PURE__ */ jsx("div", { style: { marginTop: 12, fontSize: 13, color: C8.sub }, children: s.disabled }),
17536
+ status === "ok" && /* @__PURE__ */ jsxs("div", { style: { marginTop: 12 }, children: [
17537
+ loading && /* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: C8.sub }, children: s.loading }),
17538
+ !loading && items && items.length === 0 && /* @__PURE__ */ jsx("div", { style: { fontSize: 13, color: C8.sub }, children: s.none }),
17539
+ !loading && items && items.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
17540
+ /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: C8.sub, marginBottom: 10 }, children: [
17541
+ missingCount,
17542
+ " ",
17543
+ s.missing
17544
+ ] }),
17545
+ /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", gap: 10 }, children: items.map((item) => {
17546
+ const rs = state[item.id] || {};
17547
+ return /* @__PURE__ */ jsxs(
17548
+ "div",
17549
+ {
17550
+ style: {
17551
+ display: "flex",
17552
+ gap: 12,
17553
+ alignItems: "center",
17554
+ padding: 8,
17555
+ borderRadius: 8,
17556
+ border: `1px solid ${C8.border}`,
17557
+ backgroundColor: C8.bg
17558
+ },
17559
+ children: [
17560
+ /* @__PURE__ */ jsx(
17561
+ "img",
17562
+ {
17563
+ src: item.url,
17564
+ alt: "",
17565
+ style: { width: 48, height: 48, objectFit: "cover", borderRadius: 6, flexShrink: 0, border: `1px solid ${C8.border}` }
17566
+ }
17567
+ ),
17568
+ /* @__PURE__ */ jsxs("div", { style: { flex: 1, minWidth: 0 }, children: [
17569
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: C8.sub, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }, children: item.filename }),
17570
+ rs.alt !== void 0 ? /* @__PURE__ */ jsx(
17571
+ "input",
17572
+ {
17573
+ value: rs.alt,
17574
+ onChange: (e) => setRow(item.id, { alt: e.target.value }),
17575
+ disabled: rs.applied,
17576
+ maxLength: 125,
17577
+ style: {
17578
+ width: "100%",
17579
+ marginTop: 4,
17580
+ padding: "4px 8px",
17581
+ fontSize: 12,
17582
+ borderRadius: 6,
17583
+ border: `1px solid ${C8.border}`,
17584
+ backgroundColor: C8.bg,
17585
+ color: C8.text
17586
+ }
17587
+ }
17588
+ ) : /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: C8.sub, marginTop: 4, fontStyle: "italic" }, children: "\u2014" }),
17589
+ rs.error && /* @__PURE__ */ jsx("div", { style: { fontSize: 11, color: C8.red, marginTop: 2 }, children: rs.error })
17590
+ ] }),
17591
+ /* @__PURE__ */ jsx("div", { style: { flexShrink: 0 }, children: rs.applied ? /* @__PURE__ */ jsx("span", { style: { fontSize: 11, fontWeight: 700, color: C8.green }, children: s.applied }) : rs.alt !== void 0 ? /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void apply(item), disabled: rs.busy, style: btn(C8.green), children: rs.busy ? s.generating : s.apply }) : /* @__PURE__ */ jsx("button", { type: "button", onClick: () => void generate(item), disabled: rs.busy, style: btn(C8.violet), children: rs.busy ? s.generating : s.generate }) })
17592
+ ]
17593
+ },
17594
+ item.id
17595
+ );
17596
+ }) })
17597
+ ] })
17598
+ ] })
17599
+ ] });
17600
+ }
16965
17601
  var V8 = {
16966
17602
  text: "var(--theme-text, #1a1a1a)",
16967
17603
  textSecondary: "var(--theme-elevation-600, #6b7280)",
@@ -17376,6 +18012,9 @@ function PerformanceView() {
17376
18012
  ),
17377
18013
  /* @__PURE__ */ jsx(CoreWebVitalsPanel, { locale }),
17378
18014
  /* @__PURE__ */ jsx(GscPanel, { locale }),
18015
+ /* @__PURE__ */ jsx(RankTrackingPanel, { locale }),
18016
+ /* @__PURE__ */ jsx(AlertsPanel, { locale }),
18017
+ /* @__PURE__ */ jsx(AltTextPanel, { locale }),
17379
18018
  showImport && /* @__PURE__ */ jsxs(
17380
18019
  "div",
17381
18020
  {
@@ -17899,6 +18538,183 @@ function PerformanceView() {
17899
18538
  }
17900
18539
  );
17901
18540
  }
18541
+ var C9 = {
18542
+ text: "var(--theme-text, #1a1a1a)",
18543
+ sub: "var(--theme-elevation-600, #6b7280)",
18544
+ card: "var(--theme-elevation-50, #f9fafb)",
18545
+ bg: "var(--theme-elevation-0, #fff)",
18546
+ border: "var(--theme-elevation-200, #e5e7eb)",
18547
+ violet: "#7c3aed",
18548
+ red: "#ef4444",
18549
+ blue: "#3b82f6"
18550
+ };
18551
+ var S6 = {
18552
+ fr: {
18553
+ title: "Brief de contenu IA",
18554
+ subtitle: "G\xE9n\xE8re un plan r\xE9dactionnel optimis\xE9 pour un mot-cl\xE9 (plan, entit\xE9s, questions, longueur cible).",
18555
+ placeholder: "Mot-cl\xE9 cible (ex : plombier paris)",
18556
+ generate: "G\xE9n\xE9rer le brief",
18557
+ generating: "G\xE9n\xE9ration\u2026",
18558
+ outline: "Plan sugg\xE9r\xE9",
18559
+ entities: "Entit\xE9s / termes \xE0 couvrir",
18560
+ questions: "Questions \xE0 traiter",
18561
+ links: "Id\xE9es de liens internes",
18562
+ words: "Longueur recommand\xE9e",
18563
+ wordsUnit: "mots",
18564
+ notes: "Conseils",
18565
+ noKey: "Cl\xE9 API Claude requise (ANTHROPIC_API_KEY).",
18566
+ disabled: "Fonction IA d\xE9sactiv\xE9e (features.aiFeatures)."
18567
+ },
18568
+ en: {
18569
+ title: "AI content brief",
18570
+ subtitle: "Generate an optimized writing brief for a keyword (outline, entities, questions, target length).",
18571
+ placeholder: "Target keyword (e.g. paris plumber)",
18572
+ generate: "Generate brief",
18573
+ generating: "Generating\u2026",
18574
+ outline: "Suggested outline",
18575
+ entities: "Entities / terms to cover",
18576
+ questions: "Questions to answer",
18577
+ links: "Internal link ideas",
18578
+ words: "Recommended length",
18579
+ wordsUnit: "words",
18580
+ notes: "Tips",
18581
+ noKey: "Claude API key required (ANTHROPIC_API_KEY).",
18582
+ disabled: "AI feature disabled (features.aiFeatures)."
18583
+ }
18584
+ };
18585
+ function ContentBriefPanel({ locale }) {
18586
+ const s = S6[locale] ?? S6.fr;
18587
+ const [keyword, setKeyword] = useState("");
18588
+ const [brief, setBrief] = useState(null);
18589
+ const [busy, setBusy] = useState(false);
18590
+ const [error, setError] = useState(null);
18591
+ const generate = async () => {
18592
+ if (!keyword.trim()) return;
18593
+ setBusy(true);
18594
+ setError(null);
18595
+ setBrief(null);
18596
+ try {
18597
+ const res = await fetch("/api/seo-plugin/ai-content-brief", {
18598
+ method: "POST",
18599
+ credentials: "include",
18600
+ headers: { "Content-Type": "application/json" },
18601
+ body: JSON.stringify({ keyword: keyword.trim() })
18602
+ });
18603
+ if (res.status === 404) {
18604
+ setError(s.disabled);
18605
+ return;
18606
+ }
18607
+ const json = await res.json();
18608
+ if (!res.ok) {
18609
+ setError(json.code === "no_api_key" ? s.noKey : json.error || `Error ${res.status}`);
18610
+ return;
18611
+ }
18612
+ setBrief(json.brief);
18613
+ } catch (e) {
18614
+ setError(e instanceof Error ? e.message : "Network error");
18615
+ } finally {
18616
+ setBusy(false);
18617
+ }
18618
+ };
18619
+ const card = {
18620
+ padding: 16,
18621
+ borderRadius: 12,
18622
+ border: `1px solid ${C9.border}`,
18623
+ backgroundColor: C9.card,
18624
+ marginBottom: 20
18625
+ };
18626
+ const chip = {
18627
+ display: "inline-block",
18628
+ fontSize: 11,
18629
+ padding: "3px 8px",
18630
+ borderRadius: 999,
18631
+ backgroundColor: "rgba(59,130,246,0.12)",
18632
+ color: C9.blue,
18633
+ border: `1px solid ${C9.border}`,
18634
+ margin: "0 6px 6px 0"
18635
+ };
18636
+ const h4 = { fontSize: 12, fontWeight: 800, color: C9.text, margin: "14px 0 6px", textTransform: "uppercase" };
18637
+ return /* @__PURE__ */ jsxs("div", { style: card, children: [
18638
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 16, fontWeight: 800, color: C9.text }, children: s.title }),
18639
+ /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: C9.sub, marginTop: 2, marginBottom: 12 }, children: s.subtitle }),
18640
+ /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
18641
+ /* @__PURE__ */ jsx(
18642
+ "input",
18643
+ {
18644
+ value: keyword,
18645
+ onChange: (e) => setKeyword(e.target.value),
18646
+ onKeyDown: (e) => {
18647
+ if (e.key === "Enter") void generate();
18648
+ },
18649
+ placeholder: s.placeholder,
18650
+ style: {
18651
+ flex: 1,
18652
+ minWidth: 220,
18653
+ padding: "8px 12px",
18654
+ fontSize: 13,
18655
+ borderRadius: 8,
18656
+ border: `1px solid ${C9.border}`,
18657
+ backgroundColor: C9.bg,
18658
+ color: C9.text
18659
+ }
18660
+ }
18661
+ ),
18662
+ /* @__PURE__ */ jsx(
18663
+ "button",
18664
+ {
18665
+ type: "button",
18666
+ onClick: () => void generate(),
18667
+ disabled: busy || !keyword.trim(),
18668
+ style: {
18669
+ padding: "8px 14px",
18670
+ borderRadius: 8,
18671
+ border: `1px solid ${C9.violet}`,
18672
+ backgroundColor: C9.violet,
18673
+ color: "#fff",
18674
+ fontSize: 12,
18675
+ fontWeight: 700,
18676
+ cursor: busy || !keyword.trim() ? "not-allowed" : "pointer",
18677
+ opacity: busy || !keyword.trim() ? 0.6 : 1
18678
+ },
18679
+ children: busy ? s.generating : `\u2728 ${s.generate}`
18680
+ }
18681
+ )
18682
+ ] }),
18683
+ error && /* @__PURE__ */ jsx("div", { style: { color: C9.red, fontSize: 13, fontWeight: 600, marginTop: 10 }, children: error }),
18684
+ brief && /* @__PURE__ */ jsxs("div", { style: { marginTop: 8 }, children: [
18685
+ brief.recommendedWordCount > 0 && /* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: C9.sub, marginTop: 10 }, children: [
18686
+ s.words,
18687
+ ": ",
18688
+ /* @__PURE__ */ jsxs("b", { style: { color: C9.text }, children: [
18689
+ "~",
18690
+ brief.recommendedWordCount,
18691
+ " ",
18692
+ s.wordsUnit
18693
+ ] })
18694
+ ] }),
18695
+ brief.outline.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
18696
+ /* @__PURE__ */ jsx("div", { style: h4, children: s.outline }),
18697
+ /* @__PURE__ */ jsx("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 13, color: C9.text, lineHeight: 1.6 }, children: brief.outline.map((o, i) => /* @__PURE__ */ jsx("li", { style: { marginLeft: o.level === "h3" ? 18 : 0, color: o.level === "h3" ? C9.sub : C9.text, fontWeight: o.level === "h2" ? 700 : 400 }, children: o.text }, i)) })
18698
+ ] }),
18699
+ brief.entities.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
18700
+ /* @__PURE__ */ jsx("div", { style: h4, children: s.entities }),
18701
+ /* @__PURE__ */ jsx("div", { children: brief.entities.map((e, i) => /* @__PURE__ */ jsx("span", { style: chip, children: e }, i)) })
18702
+ ] }),
18703
+ brief.questions.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
18704
+ /* @__PURE__ */ jsx("div", { style: h4, children: s.questions }),
18705
+ /* @__PURE__ */ jsx("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 13, color: C9.text, lineHeight: 1.6 }, children: brief.questions.map((q, i) => /* @__PURE__ */ jsx("li", { children: q }, i)) })
18706
+ ] }),
18707
+ brief.internalLinkIdeas.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
18708
+ /* @__PURE__ */ jsx("div", { style: h4, children: s.links }),
18709
+ /* @__PURE__ */ jsx("div", { children: brief.internalLinkIdeas.map((l, i) => /* @__PURE__ */ jsx("span", { style: chip, children: l }, i)) })
18710
+ ] }),
18711
+ brief.notes.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
18712
+ /* @__PURE__ */ jsx("div", { style: h4, children: s.notes }),
18713
+ /* @__PURE__ */ jsx("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 12, color: C9.sub, lineHeight: 1.6 }, children: brief.notes.map((n, i) => /* @__PURE__ */ jsx("li", { children: n }, i)) })
18714
+ ] })
18715
+ ] })
18716
+ ] });
18717
+ }
17902
18718
  var V9 = {
17903
18719
  text: "var(--theme-text, #1a1a1a)",
17904
18720
  textSecondary: "var(--theme-elevation-600, #6b7280)",
@@ -18261,6 +19077,7 @@ function KeywordResearchView() {
18261
19077
  ]
18262
19078
  }
18263
19079
  ),
19080
+ /* @__PURE__ */ jsx(ContentBriefPanel, { locale }),
18264
19081
  /* @__PURE__ */ jsx(
18265
19082
  "div",
18266
19083
  {
@@ -19035,7 +19852,7 @@ var controlBtnStyle = {
19035
19852
  cursor: "pointer",
19036
19853
  lineHeight: 1.4
19037
19854
  };
19038
- var C6 = {
19855
+ var C10 = {
19039
19856
  cyan: "#00E5FF",
19040
19857
  black: "#000",
19041
19858
  green: "#22c55e",
@@ -19050,10 +19867,10 @@ var C6 = {
19050
19867
  var TITLE_MIN = 30;
19051
19868
  var TITLE_MAX = 60;
19052
19869
  function getCharColor(len) {
19053
- if (len === 0) return C6.textSecondary;
19054
- if (len >= TITLE_MIN && len <= TITLE_MAX) return C6.green;
19055
- if (len > 0 && len < TITLE_MIN) return C6.orange;
19056
- return C6.red;
19870
+ if (len === 0) return C10.textSecondary;
19871
+ if (len >= TITLE_MIN && len <= TITLE_MAX) return C10.green;
19872
+ if (len > 0 && len < TITLE_MIN) return C10.orange;
19873
+ return C10.red;
19057
19874
  }
19058
19875
  function getProgressPercent(len) {
19059
19876
  if (len === 0) return 0;
@@ -19061,9 +19878,9 @@ function getProgressPercent(len) {
19061
19878
  }
19062
19879
  function getProgressColor(len) {
19063
19880
  if (len === 0) return "var(--theme-elevation-200, #e5e7eb)";
19064
- if (len >= TITLE_MIN && len <= TITLE_MAX) return C6.green;
19065
- if (len < TITLE_MIN) return C6.orange;
19066
- return C6.red;
19881
+ if (len >= TITLE_MIN && len <= TITLE_MAX) return C10.green;
19882
+ if (len < TITLE_MIN) return C10.orange;
19883
+ return C10.red;
19067
19884
  }
19068
19885
  function MetaTitleField({
19069
19886
  path,
@@ -19134,7 +19951,7 @@ function MetaTitleField({
19134
19951
  style: {
19135
19952
  fontSize: 13,
19136
19953
  fontWeight: 700,
19137
- color: C6.textPrimary
19954
+ color: C10.textPrimary
19138
19955
  },
19139
19956
  children: t.metaTitle.label
19140
19957
  }
@@ -19174,9 +19991,9 @@ function MetaTitleField({
19174
19991
  fontSize: 14,
19175
19992
  fontFamily: "inherit",
19176
19993
  borderRadius: 8,
19177
- border: `2px solid ${C6.border}`,
19178
- backgroundColor: C6.surfaceBg,
19179
- color: C6.textPrimary,
19994
+ border: `2px solid ${C10.border}`,
19995
+ backgroundColor: C10.surfaceBg,
19996
+ color: C10.textPrimary,
19180
19997
  outline: "none",
19181
19998
  boxShadow: "2px 2px 0 0 var(--theme-border-color, rgba(0,0,0,1))"
19182
19999
  }
@@ -19194,9 +20011,9 @@ function MetaTitleField({
19194
20011
  gap: 5,
19195
20012
  padding: "8px 14px",
19196
20013
  borderRadius: 8,
19197
- border: `2px solid ${C6.border}`,
19198
- backgroundColor: loading ? C6.surface50 : C6.cyan,
19199
- color: loading ? C6.textSecondary : C6.black,
20014
+ border: `2px solid ${C10.border}`,
20015
+ backgroundColor: loading ? C10.surface50 : C10.cyan,
20016
+ color: loading ? C10.textSecondary : C10.black,
19200
20017
  fontWeight: 800,
19201
20018
  fontSize: 11,
19202
20019
  textTransform: "uppercase",
@@ -19243,7 +20060,7 @@ function MetaTitleField({
19243
20060
  justifyContent: "space-between",
19244
20061
  marginTop: 4,
19245
20062
  fontSize: 10,
19246
- color: C6.textSecondary
20063
+ color: C10.textSecondary
19247
20064
  },
19248
20065
  children: [
19249
20066
  /* @__PURE__ */ jsxs("span", { children: [
@@ -19277,9 +20094,9 @@ function MetaTitleField({
19277
20094
  borderRadius: 6,
19278
20095
  fontSize: 11,
19279
20096
  fontWeight: 600,
19280
- color: C6.red,
20097
+ color: C10.red,
19281
20098
  backgroundColor: "rgba(239,68,68,0.08)",
19282
- border: `1px solid ${C6.red}`
20099
+ border: `1px solid ${C10.red}`
19283
20100
  },
19284
20101
  children: error
19285
20102
  }
@@ -19288,7 +20105,7 @@ function MetaTitleField({
19288
20105
  }
19289
20106
  );
19290
20107
  }
19291
- var C7 = {
20108
+ var C11 = {
19292
20109
  cyan: "#00E5FF",
19293
20110
  black: "#000",
19294
20111
  green: "#22c55e",
@@ -19303,10 +20120,10 @@ var C7 = {
19303
20120
  var DESC_MIN = 120;
19304
20121
  var DESC_MAX = 160;
19305
20122
  function getCharColor2(len) {
19306
- if (len === 0) return C7.textSecondary;
19307
- if (len >= DESC_MIN && len <= DESC_MAX) return C7.green;
19308
- if (len > 0 && len < DESC_MIN) return C7.orange;
19309
- return C7.red;
20123
+ if (len === 0) return C11.textSecondary;
20124
+ if (len >= DESC_MIN && len <= DESC_MAX) return C11.green;
20125
+ if (len > 0 && len < DESC_MIN) return C11.orange;
20126
+ return C11.red;
19310
20127
  }
19311
20128
  function getProgressPercent2(len) {
19312
20129
  if (len === 0) return 0;
@@ -19314,9 +20131,9 @@ function getProgressPercent2(len) {
19314
20131
  }
19315
20132
  function getProgressColor2(len) {
19316
20133
  if (len === 0) return "var(--theme-elevation-200, #e5e7eb)";
19317
- if (len >= DESC_MIN && len <= DESC_MAX) return C7.green;
19318
- if (len < DESC_MIN) return C7.orange;
19319
- return C7.red;
20134
+ if (len >= DESC_MIN && len <= DESC_MAX) return C11.green;
20135
+ if (len < DESC_MIN) return C11.orange;
20136
+ return C11.red;
19320
20137
  }
19321
20138
  function MetaDescriptionField({
19322
20139
  path,
@@ -19387,7 +20204,7 @@ function MetaDescriptionField({
19387
20204
  style: {
19388
20205
  fontSize: 13,
19389
20206
  fontWeight: 700,
19390
- color: C7.textPrimary
20207
+ color: C11.textPrimary
19391
20208
  },
19392
20209
  children: t.metaDescription.label
19393
20210
  }
@@ -19427,9 +20244,9 @@ function MetaDescriptionField({
19427
20244
  fontSize: 14,
19428
20245
  fontFamily: "inherit",
19429
20246
  borderRadius: 8,
19430
- border: `2px solid ${C7.border}`,
19431
- backgroundColor: C7.surfaceBg,
19432
- color: C7.textPrimary,
20247
+ border: `2px solid ${C11.border}`,
20248
+ backgroundColor: C11.surfaceBg,
20249
+ color: C11.textPrimary,
19433
20250
  outline: "none",
19434
20251
  resize: "vertical",
19435
20252
  lineHeight: 1.5,
@@ -19449,9 +20266,9 @@ function MetaDescriptionField({
19449
20266
  gap: 5,
19450
20267
  padding: "8px 14px",
19451
20268
  borderRadius: 8,
19452
- border: `2px solid ${C7.border}`,
19453
- backgroundColor: loading ? C7.surface50 : C7.cyan,
19454
- color: loading ? C7.textSecondary : C7.black,
20269
+ border: `2px solid ${C11.border}`,
20270
+ backgroundColor: loading ? C11.surface50 : C11.cyan,
20271
+ color: loading ? C11.textSecondary : C11.black,
19455
20272
  fontWeight: 800,
19456
20273
  fontSize: 11,
19457
20274
  textTransform: "uppercase",
@@ -19499,7 +20316,7 @@ function MetaDescriptionField({
19499
20316
  justifyContent: "space-between",
19500
20317
  marginTop: 4,
19501
20318
  fontSize: 10,
19502
- color: C7.textSecondary
20319
+ color: C11.textSecondary
19503
20320
  },
19504
20321
  children: [
19505
20322
  /* @__PURE__ */ jsxs("span", { children: [
@@ -19533,9 +20350,9 @@ function MetaDescriptionField({
19533
20350
  borderRadius: 6,
19534
20351
  fontSize: 11,
19535
20352
  fontWeight: 600,
19536
- color: C7.red,
20353
+ color: C11.red,
19537
20354
  backgroundColor: "rgba(239,68,68,0.08)",
19538
- border: `1px solid ${C7.red}`
20355
+ border: `1px solid ${C11.red}`
19539
20356
  },
19540
20357
  children: error
19541
20358
  }
@@ -19544,7 +20361,7 @@ function MetaDescriptionField({
19544
20361
  }
19545
20362
  );
19546
20363
  }
19547
- var C8 = {
20364
+ var C12 = {
19548
20365
  cyan: "#00E5FF",
19549
20366
  black: "#000",
19550
20367
  white: "#fff",
@@ -19621,8 +20438,8 @@ function MetaImageField({
19621
20438
  gap: 10,
19622
20439
  padding: "10px 14px",
19623
20440
  borderRadius: 8,
19624
- border: `2px solid ${C8.border}`,
19625
- backgroundColor: C8.surfaceBg,
20441
+ border: `2px solid ${C12.border}`,
20442
+ backgroundColor: C12.surfaceBg,
19626
20443
  boxShadow: "2px 2px 0 0 var(--theme-border-color, rgba(0,0,0,1))"
19627
20444
  },
19628
20445
  children: [
@@ -19641,7 +20458,7 @@ function MetaImageField({
19641
20458
  fontWeight: 900,
19642
20459
  backgroundColor: hasImage ? "rgba(34,197,94,0.15)" : "rgba(255,138,0,0.15)",
19643
20460
  color: hasImage ? "#16a34a" : "#d97706",
19644
- border: `1px solid ${hasImage ? C8.green : C8.orange}`
20461
+ border: `1px solid ${hasImage ? C12.green : C12.orange}`
19645
20462
  },
19646
20463
  children: hasImage ? "\u2713" : "!"
19647
20464
  }
@@ -19653,7 +20470,7 @@ function MetaImageField({
19653
20470
  style: {
19654
20471
  fontSize: 12,
19655
20472
  fontWeight: 700,
19656
- color: C8.textPrimary
20473
+ color: C12.textPrimary
19657
20474
  },
19658
20475
  children: t.metaImage.label
19659
20476
  }
@@ -19663,7 +20480,7 @@ function MetaImageField({
19663
20480
  {
19664
20481
  style: {
19665
20482
  fontSize: 10,
19666
- color: C8.textSecondary,
20483
+ color: C12.textSecondary,
19667
20484
  lineHeight: 1.4
19668
20485
  },
19669
20486
  children: hasImage ? t.metaImage.imageSet : t.metaImage.noImage
@@ -19683,9 +20500,9 @@ function MetaImageField({
19683
20500
  gap: 5,
19684
20501
  padding: "8px 14px",
19685
20502
  borderRadius: 8,
19686
- border: `2px solid ${C8.border}`,
19687
- backgroundColor: loading ? C8.surface50 : success ? C8.green : C8.cyan,
19688
- color: loading ? C8.textSecondary : success ? C8.white : C8.black,
20503
+ border: `2px solid ${C12.border}`,
20504
+ backgroundColor: loading ? C12.surface50 : success ? C12.green : C12.cyan,
20505
+ color: loading ? C12.textSecondary : success ? C12.white : C12.black,
19689
20506
  fontWeight: 800,
19690
20507
  fontSize: 11,
19691
20508
  textTransform: "uppercase",
@@ -19712,9 +20529,9 @@ function MetaImageField({
19712
20529
  borderRadius: 6,
19713
20530
  fontSize: 11,
19714
20531
  fontWeight: 600,
19715
- color: C8.red,
20532
+ color: C12.red,
19716
20533
  backgroundColor: "rgba(239,68,68,0.08)",
19717
- border: `1px solid ${C8.red}`
20534
+ border: `1px solid ${C12.red}`
19718
20535
  },
19719
20536
  children: error
19720
20537
  }
@@ -19723,7 +20540,7 @@ function MetaImageField({
19723
20540
  }
19724
20541
  );
19725
20542
  }
19726
- var C9 = {
20543
+ var C13 = {
19727
20544
  black: "#000",
19728
20545
  white: "#fff",
19729
20546
  green: "#22c55e",
@@ -19737,15 +20554,15 @@ var C9 = {
19737
20554
  function getCompletenessColor(count) {
19738
20555
  switch (count) {
19739
20556
  case 0:
19740
- return C9.red;
20557
+ return C13.red;
19741
20558
  case 1:
19742
- return C9.orange;
20559
+ return C13.orange;
19743
20560
  case 2:
19744
- return C9.yellow;
20561
+ return C13.yellow;
19745
20562
  case 3:
19746
- return C9.green;
20563
+ return C13.green;
19747
20564
  default:
19748
- return C9.textSecondary;
20565
+ return C13.textSecondary;
19749
20566
  }
19750
20567
  }
19751
20568
  function getCompletenessLabel(count, ov) {
@@ -19803,8 +20620,8 @@ function OverviewField({
19803
20620
  fontFamily: "var(--font-body, Inter, system-ui, sans-serif)",
19804
20621
  padding: "12px 14px",
19805
20622
  borderRadius: 10,
19806
- border: `2px solid ${C9.border}`,
19807
- backgroundColor: C9.surfaceBg,
20623
+ border: `2px solid ${C13.border}`,
20624
+ backgroundColor: C13.surfaceBg,
19808
20625
  boxShadow: "3px 3px 0 0 var(--theme-border-color, rgba(0,0,0,1))",
19809
20626
  marginBottom: 12
19810
20627
  },
@@ -19827,7 +20644,7 @@ function OverviewField({
19827
20644
  fontWeight: 800,
19828
20645
  textTransform: "uppercase",
19829
20646
  letterSpacing: "0.04em",
19830
- color: C9.textPrimary
20647
+ color: C13.textPrimary
19831
20648
  },
19832
20649
  children: t.overview.metaCompleteness
19833
20650
  }
@@ -19842,8 +20659,8 @@ function OverviewField({
19842
20659
  fontSize: 11,
19843
20660
  fontWeight: 800,
19844
20661
  backgroundColor: completenessColor,
19845
- color: completenessColor === C9.yellow ? C9.black : C9.white,
19846
- border: `2px solid ${C9.border}`,
20662
+ color: completenessColor === C13.yellow ? C13.black : C13.white,
20663
+ border: `2px solid ${C13.border}`,
19847
20664
  textTransform: "uppercase",
19848
20665
  letterSpacing: "0.03em"
19849
20666
  },
@@ -19941,7 +20758,7 @@ function OverviewField({
19941
20758
  style: {
19942
20759
  fontSize: 12,
19943
20760
  fontWeight: 600,
19944
- color: item.filled ? C9.textPrimary : C9.textSecondary
20761
+ color: item.filled ? C13.textPrimary : C13.textSecondary
19945
20762
  },
19946
20763
  children: item.label
19947
20764
  }
@@ -19953,7 +20770,7 @@ function OverviewField({
19953
20770
  marginLeft: "auto",
19954
20771
  fontSize: 10,
19955
20772
  fontWeight: 700,
19956
- color: item.filled ? C9.green : C9.red,
20773
+ color: item.filled ? C13.green : C13.red,
19957
20774
  textTransform: "uppercase",
19958
20775
  letterSpacing: "0.03em"
19959
20776
  },
@@ -19970,7 +20787,7 @@ function OverviewField({
19970
20787
  }
19971
20788
  );
19972
20789
  }
19973
- var C10 = {
20790
+ var C14 = {
19974
20791
  cyan: "#00E5FF",
19975
20792
  black: "#000",
19976
20793
  white: "#fff",
@@ -19989,10 +20806,10 @@ var G = {
19989
20806
  descGrey: "#4d5156",
19990
20807
  faviconBg: "#e8eaed"};
19991
20808
  function charCountColor2(len, min, max) {
19992
- if (len >= min && len <= max) return C10.green;
19993
- if (len > 0 && len < min) return C10.orange;
19994
- if (len > max) return C10.red;
19995
- return C10.textSecondary;
20809
+ if (len >= min && len <= max) return C14.green;
20810
+ if (len > 0 && len < min) return C14.orange;
20811
+ if (len > max) return C14.red;
20812
+ return C14.textSecondary;
19996
20813
  }
19997
20814
  function truncateText(text, maxChars) {
19998
20815
  if (text.length <= maxChars) return text;
@@ -20047,8 +20864,8 @@ function SerpPreview({
20047
20864
  padding: "10px 12px",
20048
20865
  cursor: "pointer",
20049
20866
  borderRadius: 8,
20050
- border: `2px solid ${C10.border}`,
20051
- backgroundColor: C10.surface50,
20867
+ border: `2px solid ${C14.border}`,
20868
+ backgroundColor: C14.surface50,
20052
20869
  userSelect: "none"
20053
20870
  },
20054
20871
  children: [
@@ -20063,7 +20880,7 @@ function SerpPreview({
20063
20880
  fontWeight: 800,
20064
20881
  textTransform: "uppercase",
20065
20882
  letterSpacing: "0.04em",
20066
- color: C10.textPrimary
20883
+ color: C14.textPrimary
20067
20884
  },
20068
20885
  children: [
20069
20886
  /* @__PURE__ */ jsxs(
@@ -20095,7 +20912,7 @@ function SerpPreview({
20095
20912
  transition: "transform 0.2s",
20096
20913
  display: "inline-block",
20097
20914
  transform: open ? "rotate(90deg)" : "none",
20098
- color: C10.textSecondary
20915
+ color: C14.textSecondary
20099
20916
  },
20100
20917
  children: "\u25B6"
20101
20918
  }
@@ -20128,10 +20945,10 @@ function SerpPreview({
20128
20945
  "div",
20129
20946
  {
20130
20947
  style: {
20131
- backgroundColor: C10.white,
20132
- border: `2px solid ${C10.border}`,
20948
+ backgroundColor: C14.white,
20949
+ border: `2px solid ${C14.border}`,
20133
20950
  borderRadius: 12,
20134
- boxShadow: `3px 3px 0 0 ${C10.border}`,
20951
+ boxShadow: `3px 3px 0 0 ${C14.border}`,
20135
20952
  padding: isDesktop ? 20 : 14,
20136
20953
  maxWidth: isDesktop ? 650 : 380,
20137
20954
  overflow: "hidden"
@@ -20194,7 +21011,7 @@ function SerpPreview({
20194
21011
  style: {
20195
21012
  fontSize: 14,
20196
21013
  fontWeight: 400,
20197
- color: C10.black,
21014
+ color: C14.black,
20198
21015
  lineHeight: 1.3,
20199
21016
  whiteSpace: "nowrap",
20200
21017
  overflow: "hidden",
@@ -20258,7 +21075,7 @@ function SerpPreview({
20258
21075
  "span",
20259
21076
  {
20260
21077
  style: {
20261
- color: C10.textSecondary,
21078
+ color: C14.textSecondary,
20262
21079
  fontStyle: "italic",
20263
21080
  fontSize: titleFontSize - 2
20264
21081
  },
@@ -20287,7 +21104,7 @@ function SerpPreview({
20287
21104
  "span",
20288
21105
  {
20289
21106
  style: {
20290
- color: C10.textSecondary,
21107
+ color: C14.textSecondary,
20291
21108
  fontStyle: "italic",
20292
21109
  fontSize: descFontSize - 1
20293
21110
  },
@@ -20320,7 +21137,7 @@ function SerpPreview({
20320
21137
  fontSize: 11
20321
21138
  },
20322
21139
  children: [
20323
- /* @__PURE__ */ jsx("span", { style: { color: C10.textSecondary, fontWeight: 600 }, children: t.serpPreview.previewTitle }),
21140
+ /* @__PURE__ */ jsx("span", { style: { color: C14.textSecondary, fontWeight: 600 }, children: t.serpPreview.previewTitle }),
20324
21141
  /* @__PURE__ */ jsxs(
20325
21142
  "span",
20326
21143
  {
@@ -20349,7 +21166,7 @@ function SerpPreview({
20349
21166
  fontSize: 11
20350
21167
  },
20351
21168
  children: [
20352
- /* @__PURE__ */ jsx("span", { style: { color: C10.textSecondary, fontWeight: 600 }, children: t.serpPreview.previewDescription }),
21169
+ /* @__PURE__ */ jsx("span", { style: { color: C14.textSecondary, fontWeight: 600 }, children: t.serpPreview.previewDescription }),
20353
21170
  /* @__PURE__ */ jsxs(
20354
21171
  "span",
20355
21172
  {
@@ -20378,14 +21195,14 @@ function SerpPreview({
20378
21195
  fontSize: 11
20379
21196
  },
20380
21197
  children: [
20381
- /* @__PURE__ */ jsx("span", { style: { color: C10.textSecondary, fontWeight: 600 }, children: t.serpPreview.url }),
21198
+ /* @__PURE__ */ jsx("span", { style: { color: C14.textSecondary, fontWeight: 600 }, children: t.serpPreview.url }),
20382
21199
  /* @__PURE__ */ jsxs(
20383
21200
  "span",
20384
21201
  {
20385
21202
  style: {
20386
21203
  fontWeight: 700,
20387
21204
  fontVariantNumeric: "tabular-nums",
20388
- color: fullUrl.length <= 75 ? C10.green : C10.red
21205
+ color: fullUrl.length <= 75 ? C14.green : C14.red
20389
21206
  },
20390
21207
  children: [
20391
21208
  fullUrl.length,
@@ -20420,13 +21237,13 @@ function DeviceButton({
20420
21237
  gap: 5,
20421
21238
  padding: "4px 12px",
20422
21239
  borderRadius: 6,
20423
- border: `2px solid ${C10.border}`,
21240
+ border: `2px solid ${C14.border}`,
20424
21241
  fontSize: 11,
20425
21242
  fontWeight: 700,
20426
21243
  cursor: "pointer",
20427
- backgroundColor: active ? C10.cyan : C10.surfaceBg,
20428
- color: active ? C10.black : C10.textPrimary,
20429
- boxShadow: active ? `2px 2px 0 0 ${C10.border}` : "none",
21244
+ backgroundColor: active ? C14.cyan : C14.surfaceBg,
21245
+ color: active ? C14.black : C14.textPrimary,
21246
+ boxShadow: active ? `2px 2px 0 0 ${C14.border}` : "none",
20430
21247
  transition: "background-color 0.15s"
20431
21248
  },
20432
21249
  children: [