@consilioweb/payload-seo-analyzer 1.10.0 → 1.12.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.cjs CHANGED
@@ -1065,6 +1065,16 @@ var fr = {
1065
1065
  pagesAnalyzed: "pages analys\xE9es",
1066
1066
  markCornerstone: "Marquer pilier",
1067
1067
  unmarkCornerstone: "D\xE9marquer pilier",
1068
+ bulkOptimizeMeta: "Optimiser m\xE9ta (IA)",
1069
+ bulkOptimizing: "Analyse\u2026",
1070
+ bulkConfirm: "Confirmer ?",
1071
+ bulkPreviewTitle: "Corrections m\xE9ta propos\xE9es",
1072
+ bulkApply: "Appliquer",
1073
+ bulkApplying: "Application\u2026",
1074
+ bulkCancel: "Annuler",
1075
+ bulkExport: "Exporter CSV",
1076
+ bulkNoChanges: "Aucune correction n\xE9cessaire sur la s\xE9lection.",
1077
+ bulkCappedNote: "limite atteinte (affine la s\xE9lection)",
1068
1078
  searchPlaceholder: "Rechercher (titre, slug, keyword)...",
1069
1079
  allCollections: "Toutes les collections",
1070
1080
  allScores: "Tous les scores",
@@ -1657,6 +1667,16 @@ var en = {
1657
1667
  pagesAnalyzed: "pages analyzed",
1658
1668
  markCornerstone: "Mark as cornerstone",
1659
1669
  unmarkCornerstone: "Unmark cornerstone",
1670
+ bulkOptimizeMeta: "Optimize meta (AI)",
1671
+ bulkOptimizing: "Analyzing\u2026",
1672
+ bulkConfirm: "Confirm?",
1673
+ bulkPreviewTitle: "Proposed meta corrections",
1674
+ bulkApply: "Apply",
1675
+ bulkApplying: "Applying\u2026",
1676
+ bulkCancel: "Cancel",
1677
+ bulkExport: "Export CSV",
1678
+ bulkNoChanges: "No corrections needed on the selection.",
1679
+ bulkCappedNote: "limit reached (narrow the selection)",
1660
1680
  searchPlaceholder: "Search (title, slug, keyword)...",
1661
1681
  allCollections: "All collections",
1662
1682
  allScores: "All scores",
@@ -9201,6 +9221,8 @@ function BulkActionBar({
9201
9221
  onExportCsv,
9202
9222
  onMarkCornerstone,
9203
9223
  onUnmarkCornerstone,
9224
+ onOptimizeMeta,
9225
+ optimizing,
9204
9226
  t
9205
9227
  }) {
9206
9228
  if (count === 0) return null;
@@ -9248,6 +9270,17 @@ function BulkActionBar({
9248
9270
  children: t.common.exportCsv
9249
9271
  }
9250
9272
  ),
9273
+ /* @__PURE__ */ jsxRuntime.jsx(
9274
+ "button",
9275
+ {
9276
+ onClick: () => {
9277
+ if (!optimizing) onOptimizeMeta();
9278
+ },
9279
+ disabled: optimizing,
9280
+ style: { ...btnBase, backgroundColor: "#7c3aed", color: "#fff", opacity: optimizing ? 0.6 : 1 },
9281
+ children: optimizing ? t.seoView.bulkOptimizing : `\u2728 ${t.seoView.bulkOptimizeMeta}`
9282
+ }
9283
+ ),
9251
9284
  /* @__PURE__ */ jsxRuntime.jsx(
9252
9285
  "button",
9253
9286
  {
@@ -9288,6 +9321,9 @@ function SeoView() {
9288
9321
  const [expandedId, setExpandedId] = React4.useState(null);
9289
9322
  const [saving, setSaving] = React4.useState(false);
9290
9323
  const [saveError, setSaveError] = React4.useState(null);
9324
+ const [bulkOptimizing, setBulkOptimizing] = React4.useState(false);
9325
+ const [bulkApplying, setBulkApplying] = React4.useState(false);
9326
+ const [bulkPreview, setBulkPreview] = React4.useState(null);
9291
9327
  const PAGE_SIZE = 50;
9292
9328
  const fetchAudit = React4.useCallback(async (forceRefresh = false) => {
9293
9329
  setLoading(true);
@@ -9508,6 +9544,84 @@ function SeoView() {
9508
9544
  },
9509
9545
  [selectedIds, fetchAudit]
9510
9546
  );
9547
+ const handleBulkOptimizeMeta = React4.useCallback(async () => {
9548
+ const ids = Array.from(selectedIds).filter((k) => !k.startsWith("global:"));
9549
+ if (ids.length === 0) return;
9550
+ setBulkOptimizing(true);
9551
+ setBulkPreview(null);
9552
+ try {
9553
+ const res = await fetch("/api/seo-plugin/ai-optimize-bulk", {
9554
+ method: "POST",
9555
+ headers: { "Content-Type": "application/json" },
9556
+ credentials: "include",
9557
+ body: JSON.stringify({ ids, apply: false, limit: 100 })
9558
+ });
9559
+ if (res.ok) {
9560
+ const data = await res.json();
9561
+ const changed = (data.results || []).filter((r) => r.changed && !r.error);
9562
+ setBulkPreview({ results: changed, capped: !!data.capped, total: data.processed || 0 });
9563
+ }
9564
+ } catch {
9565
+ }
9566
+ setBulkOptimizing(false);
9567
+ }, [selectedIds]);
9568
+ const handleBulkApplyPreview = React4.useCallback(async () => {
9569
+ if (!bulkPreview || bulkPreview.results.length === 0) return;
9570
+ setBulkApplying(true);
9571
+ const corrections = bulkPreview.results.map((r) => ({
9572
+ collection: r.collection,
9573
+ id: r.id,
9574
+ metaTitle: r.after.metaTitle,
9575
+ metaDescription: r.after.metaDescription,
9576
+ focusKeyword: r.after.focusKeyword
9577
+ }));
9578
+ try {
9579
+ await fetch("/api/seo-plugin/ai-optimize-bulk", {
9580
+ method: "POST",
9581
+ headers: { "Content-Type": "application/json" },
9582
+ credentials: "include",
9583
+ body: JSON.stringify({ corrections, apply: true, limit: 100 })
9584
+ });
9585
+ } catch {
9586
+ }
9587
+ setBulkApplying(false);
9588
+ setBulkPreview(null);
9589
+ setSelectedIds(/* @__PURE__ */ new Set());
9590
+ fetchAudit();
9591
+ }, [bulkPreview, fetchAudit]);
9592
+ const handleExportBulkPreviewCsv = React4.useCallback(() => {
9593
+ if (!bulkPreview) return;
9594
+ const header = [
9595
+ "collection",
9596
+ "id",
9597
+ "title",
9598
+ "before_title",
9599
+ "after_title",
9600
+ "before_description",
9601
+ "after_description",
9602
+ "before_keyword",
9603
+ "after_keyword"
9604
+ ];
9605
+ const rows = bulkPreview.results.map((r) => [
9606
+ r.collection,
9607
+ r.id,
9608
+ r.title,
9609
+ r.before.metaTitle,
9610
+ r.after.metaTitle,
9611
+ r.before.metaDescription,
9612
+ r.after.metaDescription,
9613
+ r.before.focusKeyword,
9614
+ r.after.focusKeyword
9615
+ ]);
9616
+ const csv = [header, ...rows].map((row) => row.map((c) => `"${String(c ?? "").replace(/"/g, '""')}"`).join(",")).join("\n");
9617
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
9618
+ const url = URL.createObjectURL(blob);
9619
+ const a = document.createElement("a");
9620
+ a.href = url;
9621
+ a.download = `seo-bulk-optimize-${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}.csv`;
9622
+ a.click();
9623
+ URL.revokeObjectURL(url);
9624
+ }, [bulkPreview]);
9511
9625
  const handleInlineSave = React4.useCallback(
9512
9626
  async (item, metaTitle, metaDescription) => {
9513
9627
  setSaving(true);
@@ -10256,8 +10370,100 @@ function SeoView() {
10256
10370
  onExportCsv: handleBulkExportCsv,
10257
10371
  onMarkCornerstone: () => handleBulkCornerstone(true),
10258
10372
  onUnmarkCornerstone: () => handleBulkCornerstone(false),
10373
+ onOptimizeMeta: handleBulkOptimizeMeta,
10374
+ optimizing: bulkOptimizing,
10259
10375
  t
10260
10376
  }
10377
+ ),
10378
+ bulkPreview && /* @__PURE__ */ jsxRuntime.jsx(
10379
+ "div",
10380
+ {
10381
+ style: {
10382
+ position: "fixed",
10383
+ inset: 0,
10384
+ backgroundColor: "rgba(0,0,0,0.5)",
10385
+ zIndex: 100,
10386
+ display: "flex",
10387
+ alignItems: "center",
10388
+ justifyContent: "center",
10389
+ padding: 24,
10390
+ fontFamily: "var(--font-body, system-ui)"
10391
+ },
10392
+ onClick: () => {
10393
+ if (!bulkApplying) setBulkPreview(null);
10394
+ },
10395
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
10396
+ "div",
10397
+ {
10398
+ onClick: (e) => e.stopPropagation(),
10399
+ style: {
10400
+ width: "min(900px, 100%)",
10401
+ maxHeight: "85vh",
10402
+ display: "flex",
10403
+ flexDirection: "column",
10404
+ backgroundColor: V2.bgCard,
10405
+ border: `1px solid ${V2.border}`,
10406
+ borderRadius: 12,
10407
+ overflow: "hidden"
10408
+ },
10409
+ children: [
10410
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "16px 20px", borderBottom: `1px solid ${V2.border}` }, children: [
10411
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 16, fontWeight: 800, color: V2.text }, children: t.seoView.bulkPreviewTitle }),
10412
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 12, color: V2.textSecondary, marginTop: 4 }, children: [
10413
+ bulkPreview.results.length,
10414
+ " / ",
10415
+ bulkPreview.total,
10416
+ bulkPreview.capped ? ` \xB7 ${t.seoView.bulkCappedNote}` : ""
10417
+ ] })
10418
+ ] }),
10419
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { overflowY: "auto", padding: "8px 20px", flex: 1 }, children: [
10420
+ bulkPreview.results.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 24, textAlign: "center", color: V2.textSecondary, fontSize: 14 }, children: t.seoView.bulkNoChanges }),
10421
+ bulkPreview.results.map((r) => {
10422
+ const fields = [];
10423
+ if (r.before.metaTitle !== r.after.metaTitle) fields.push({ label: "Title", before: r.before.metaTitle, after: r.after.metaTitle });
10424
+ if (r.before.metaDescription !== r.after.metaDescription) fields.push({ label: "Description", before: r.before.metaDescription, after: r.after.metaDescription });
10425
+ if (r.before.focusKeyword !== r.after.focusKeyword) fields.push({ label: "Keyword", before: r.before.focusKeyword, after: r.after.focusKeyword });
10426
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "10px 0", borderBottom: `1px solid ${V2.border}` }, children: [
10427
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 13, fontWeight: 700, color: V2.text, marginBottom: 6 }, children: [
10428
+ r.title || `${r.collection}/${r.id}`,
10429
+ " ",
10430
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, color: V2.textSecondary, fontWeight: 400 }, children: r.collection })
10431
+ ] }),
10432
+ fields.map((f, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: 4, fontSize: 12 }, children: [
10433
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 10, fontWeight: 700, color: V2.textSecondary, textTransform: "uppercase" }, children: f.label }),
10434
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: V2.red, textDecoration: f.before ? "line-through" : "none", opacity: 0.75 }, children: f.before || t.seoAnalyzer.emptyValue }),
10435
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: V2.green, fontWeight: 600 }, children: f.after || t.seoAnalyzer.emptyValue })
10436
+ ] }, i))
10437
+ ] }, `${r.collection}::${r.id}`);
10438
+ })
10439
+ ] }),
10440
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "14px 20px", borderTop: `1px solid ${V2.border}`, display: "flex", justifyContent: "space-between", gap: 8 }, children: [
10441
+ /* @__PURE__ */ jsxRuntime.jsx(
10442
+ "button",
10443
+ {
10444
+ onClick: handleExportBulkPreviewCsv,
10445
+ disabled: bulkPreview.results.length === 0,
10446
+ style: { ...btnBase, backgroundColor: V2.cyan, color: "#000", opacity: bulkPreview.results.length === 0 ? 0.5 : 1 },
10447
+ children: t.seoView.bulkExport
10448
+ }
10449
+ ),
10450
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8 }, children: [
10451
+ /* @__PURE__ */ jsxRuntime.jsx("button", { onClick: () => setBulkPreview(null), disabled: bulkApplying, style: { ...btnBase, backgroundColor: V2.bg, color: V2.text }, children: t.seoView.bulkCancel }),
10452
+ /* @__PURE__ */ jsxRuntime.jsx(
10453
+ "button",
10454
+ {
10455
+ onClick: handleBulkApplyPreview,
10456
+ disabled: bulkApplying || bulkPreview.results.length === 0,
10457
+ style: { ...btnBase, backgroundColor: "#7c3aed", color: "#fff", opacity: bulkApplying || bulkPreview.results.length === 0 ? 0.6 : 1 },
10458
+ children: bulkApplying ? t.seoView.bulkApplying : `${t.seoView.bulkApply} (${bulkPreview.results.length})`
10459
+ }
10460
+ )
10461
+ ] })
10462
+ ] })
10463
+ ]
10464
+ }
10465
+ )
10466
+ }
10261
10467
  )
10262
10468
  ]
10263
10469
  }
@@ -18475,6 +18681,183 @@ function PerformanceView() {
18475
18681
  }
18476
18682
  );
18477
18683
  }
18684
+ var C9 = {
18685
+ text: "var(--theme-text, #1a1a1a)",
18686
+ sub: "var(--theme-elevation-600, #6b7280)",
18687
+ card: "var(--theme-elevation-50, #f9fafb)",
18688
+ bg: "var(--theme-elevation-0, #fff)",
18689
+ border: "var(--theme-elevation-200, #e5e7eb)",
18690
+ violet: "#7c3aed",
18691
+ red: "#ef4444",
18692
+ blue: "#3b82f6"
18693
+ };
18694
+ var S6 = {
18695
+ fr: {
18696
+ title: "Brief de contenu IA",
18697
+ subtitle: "G\xE9n\xE8re un plan r\xE9dactionnel optimis\xE9 pour un mot-cl\xE9 (plan, entit\xE9s, questions, longueur cible).",
18698
+ placeholder: "Mot-cl\xE9 cible (ex : plombier paris)",
18699
+ generate: "G\xE9n\xE9rer le brief",
18700
+ generating: "G\xE9n\xE9ration\u2026",
18701
+ outline: "Plan sugg\xE9r\xE9",
18702
+ entities: "Entit\xE9s / termes \xE0 couvrir",
18703
+ questions: "Questions \xE0 traiter",
18704
+ links: "Id\xE9es de liens internes",
18705
+ words: "Longueur recommand\xE9e",
18706
+ wordsUnit: "mots",
18707
+ notes: "Conseils",
18708
+ noKey: "Cl\xE9 API Claude requise (ANTHROPIC_API_KEY).",
18709
+ disabled: "Fonction IA d\xE9sactiv\xE9e (features.aiFeatures)."
18710
+ },
18711
+ en: {
18712
+ title: "AI content brief",
18713
+ subtitle: "Generate an optimized writing brief for a keyword (outline, entities, questions, target length).",
18714
+ placeholder: "Target keyword (e.g. paris plumber)",
18715
+ generate: "Generate brief",
18716
+ generating: "Generating\u2026",
18717
+ outline: "Suggested outline",
18718
+ entities: "Entities / terms to cover",
18719
+ questions: "Questions to answer",
18720
+ links: "Internal link ideas",
18721
+ words: "Recommended length",
18722
+ wordsUnit: "words",
18723
+ notes: "Tips",
18724
+ noKey: "Claude API key required (ANTHROPIC_API_KEY).",
18725
+ disabled: "AI feature disabled (features.aiFeatures)."
18726
+ }
18727
+ };
18728
+ function ContentBriefPanel({ locale }) {
18729
+ const s = S6[locale] ?? S6.fr;
18730
+ const [keyword, setKeyword] = React4.useState("");
18731
+ const [brief, setBrief] = React4.useState(null);
18732
+ const [busy, setBusy] = React4.useState(false);
18733
+ const [error, setError] = React4.useState(null);
18734
+ const generate = async () => {
18735
+ if (!keyword.trim()) return;
18736
+ setBusy(true);
18737
+ setError(null);
18738
+ setBrief(null);
18739
+ try {
18740
+ const res = await fetch("/api/seo-plugin/ai-content-brief", {
18741
+ method: "POST",
18742
+ credentials: "include",
18743
+ headers: { "Content-Type": "application/json" },
18744
+ body: JSON.stringify({ keyword: keyword.trim() })
18745
+ });
18746
+ if (res.status === 404) {
18747
+ setError(s.disabled);
18748
+ return;
18749
+ }
18750
+ const json = await res.json();
18751
+ if (!res.ok) {
18752
+ setError(json.code === "no_api_key" ? s.noKey : json.error || `Error ${res.status}`);
18753
+ return;
18754
+ }
18755
+ setBrief(json.brief);
18756
+ } catch (e) {
18757
+ setError(e instanceof Error ? e.message : "Network error");
18758
+ } finally {
18759
+ setBusy(false);
18760
+ }
18761
+ };
18762
+ const card = {
18763
+ padding: 16,
18764
+ borderRadius: 12,
18765
+ border: `1px solid ${C9.border}`,
18766
+ backgroundColor: C9.card,
18767
+ marginBottom: 20
18768
+ };
18769
+ const chip = {
18770
+ display: "inline-block",
18771
+ fontSize: 11,
18772
+ padding: "3px 8px",
18773
+ borderRadius: 999,
18774
+ backgroundColor: "rgba(59,130,246,0.12)",
18775
+ color: C9.blue,
18776
+ border: `1px solid ${C9.border}`,
18777
+ margin: "0 6px 6px 0"
18778
+ };
18779
+ const h4 = { fontSize: 12, fontWeight: 800, color: C9.text, margin: "14px 0 6px", textTransform: "uppercase" };
18780
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: card, children: [
18781
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 16, fontWeight: 800, color: C9.text }, children: s.title }),
18782
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { fontSize: 12, color: C9.sub, marginTop: 2, marginBottom: 12 }, children: s.subtitle }),
18783
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", gap: 8, flexWrap: "wrap" }, children: [
18784
+ /* @__PURE__ */ jsxRuntime.jsx(
18785
+ "input",
18786
+ {
18787
+ value: keyword,
18788
+ onChange: (e) => setKeyword(e.target.value),
18789
+ onKeyDown: (e) => {
18790
+ if (e.key === "Enter") void generate();
18791
+ },
18792
+ placeholder: s.placeholder,
18793
+ style: {
18794
+ flex: 1,
18795
+ minWidth: 220,
18796
+ padding: "8px 12px",
18797
+ fontSize: 13,
18798
+ borderRadius: 8,
18799
+ border: `1px solid ${C9.border}`,
18800
+ backgroundColor: C9.bg,
18801
+ color: C9.text
18802
+ }
18803
+ }
18804
+ ),
18805
+ /* @__PURE__ */ jsxRuntime.jsx(
18806
+ "button",
18807
+ {
18808
+ type: "button",
18809
+ onClick: () => void generate(),
18810
+ disabled: busy || !keyword.trim(),
18811
+ style: {
18812
+ padding: "8px 14px",
18813
+ borderRadius: 8,
18814
+ border: `1px solid ${C9.violet}`,
18815
+ backgroundColor: C9.violet,
18816
+ color: "#fff",
18817
+ fontSize: 12,
18818
+ fontWeight: 700,
18819
+ cursor: busy || !keyword.trim() ? "not-allowed" : "pointer",
18820
+ opacity: busy || !keyword.trim() ? 0.6 : 1
18821
+ },
18822
+ children: busy ? s.generating : `\u2728 ${s.generate}`
18823
+ }
18824
+ )
18825
+ ] }),
18826
+ error && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: C9.red, fontSize: 13, fontWeight: 600, marginTop: 10 }, children: error }),
18827
+ brief && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginTop: 8 }, children: [
18828
+ brief.recommendedWordCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { fontSize: 12, color: C9.sub, marginTop: 10 }, children: [
18829
+ s.words,
18830
+ ": ",
18831
+ /* @__PURE__ */ jsxRuntime.jsxs("b", { style: { color: C9.text }, children: [
18832
+ "~",
18833
+ brief.recommendedWordCount,
18834
+ " ",
18835
+ s.wordsUnit
18836
+ ] })
18837
+ ] }),
18838
+ brief.outline.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18839
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: h4, children: s.outline }),
18840
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 13, color: C9.text, lineHeight: 1.6 }, children: brief.outline.map((o, i) => /* @__PURE__ */ jsxRuntime.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)) })
18841
+ ] }),
18842
+ brief.entities.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18843
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: h4, children: s.entities }),
18844
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: brief.entities.map((e, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: chip, children: e }, i)) })
18845
+ ] }),
18846
+ brief.questions.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18847
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: h4, children: s.questions }),
18848
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 13, color: C9.text, lineHeight: 1.6 }, children: brief.questions.map((q, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: q }, i)) })
18849
+ ] }),
18850
+ brief.internalLinkIdeas.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18851
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: h4, children: s.links }),
18852
+ /* @__PURE__ */ jsxRuntime.jsx("div", { children: brief.internalLinkIdeas.map((l, i) => /* @__PURE__ */ jsxRuntime.jsx("span", { style: chip, children: l }, i)) })
18853
+ ] }),
18854
+ brief.notes.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
18855
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: h4, children: s.notes }),
18856
+ /* @__PURE__ */ jsxRuntime.jsx("ul", { style: { margin: 0, paddingLeft: 18, fontSize: 12, color: C9.sub, lineHeight: 1.6 }, children: brief.notes.map((n, i) => /* @__PURE__ */ jsxRuntime.jsx("li", { children: n }, i)) })
18857
+ ] })
18858
+ ] })
18859
+ ] });
18860
+ }
18478
18861
  var V9 = {
18479
18862
  text: "var(--theme-text, #1a1a1a)",
18480
18863
  textSecondary: "var(--theme-elevation-600, #6b7280)",
@@ -18837,6 +19220,7 @@ function KeywordResearchView() {
18837
19220
  ]
18838
19221
  }
18839
19222
  ),
19223
+ /* @__PURE__ */ jsxRuntime.jsx(ContentBriefPanel, { locale }),
18840
19224
  /* @__PURE__ */ jsxRuntime.jsx(
18841
19225
  "div",
18842
19226
  {
@@ -19611,7 +19995,7 @@ var controlBtnStyle = {
19611
19995
  cursor: "pointer",
19612
19996
  lineHeight: 1.4
19613
19997
  };
19614
- var C9 = {
19998
+ var C10 = {
19615
19999
  cyan: "#00E5FF",
19616
20000
  black: "#000",
19617
20001
  green: "#22c55e",
@@ -19626,10 +20010,10 @@ var C9 = {
19626
20010
  var TITLE_MIN = 30;
19627
20011
  var TITLE_MAX = 60;
19628
20012
  function getCharColor(len) {
19629
- if (len === 0) return C9.textSecondary;
19630
- if (len >= TITLE_MIN && len <= TITLE_MAX) return C9.green;
19631
- if (len > 0 && len < TITLE_MIN) return C9.orange;
19632
- return C9.red;
20013
+ if (len === 0) return C10.textSecondary;
20014
+ if (len >= TITLE_MIN && len <= TITLE_MAX) return C10.green;
20015
+ if (len > 0 && len < TITLE_MIN) return C10.orange;
20016
+ return C10.red;
19633
20017
  }
19634
20018
  function getProgressPercent(len) {
19635
20019
  if (len === 0) return 0;
@@ -19637,9 +20021,9 @@ function getProgressPercent(len) {
19637
20021
  }
19638
20022
  function getProgressColor(len) {
19639
20023
  if (len === 0) return "var(--theme-elevation-200, #e5e7eb)";
19640
- if (len >= TITLE_MIN && len <= TITLE_MAX) return C9.green;
19641
- if (len < TITLE_MIN) return C9.orange;
19642
- return C9.red;
20024
+ if (len >= TITLE_MIN && len <= TITLE_MAX) return C10.green;
20025
+ if (len < TITLE_MIN) return C10.orange;
20026
+ return C10.red;
19643
20027
  }
19644
20028
  function MetaTitleField({
19645
20029
  path,
@@ -19710,7 +20094,7 @@ function MetaTitleField({
19710
20094
  style: {
19711
20095
  fontSize: 13,
19712
20096
  fontWeight: 700,
19713
- color: C9.textPrimary
20097
+ color: C10.textPrimary
19714
20098
  },
19715
20099
  children: t.metaTitle.label
19716
20100
  }
@@ -19750,9 +20134,9 @@ function MetaTitleField({
19750
20134
  fontSize: 14,
19751
20135
  fontFamily: "inherit",
19752
20136
  borderRadius: 8,
19753
- border: `2px solid ${C9.border}`,
19754
- backgroundColor: C9.surfaceBg,
19755
- color: C9.textPrimary,
20137
+ border: `2px solid ${C10.border}`,
20138
+ backgroundColor: C10.surfaceBg,
20139
+ color: C10.textPrimary,
19756
20140
  outline: "none",
19757
20141
  boxShadow: "2px 2px 0 0 var(--theme-border-color, rgba(0,0,0,1))"
19758
20142
  }
@@ -19770,9 +20154,9 @@ function MetaTitleField({
19770
20154
  gap: 5,
19771
20155
  padding: "8px 14px",
19772
20156
  borderRadius: 8,
19773
- border: `2px solid ${C9.border}`,
19774
- backgroundColor: loading ? C9.surface50 : C9.cyan,
19775
- color: loading ? C9.textSecondary : C9.black,
20157
+ border: `2px solid ${C10.border}`,
20158
+ backgroundColor: loading ? C10.surface50 : C10.cyan,
20159
+ color: loading ? C10.textSecondary : C10.black,
19776
20160
  fontWeight: 800,
19777
20161
  fontSize: 11,
19778
20162
  textTransform: "uppercase",
@@ -19819,7 +20203,7 @@ function MetaTitleField({
19819
20203
  justifyContent: "space-between",
19820
20204
  marginTop: 4,
19821
20205
  fontSize: 10,
19822
- color: C9.textSecondary
20206
+ color: C10.textSecondary
19823
20207
  },
19824
20208
  children: [
19825
20209
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
@@ -19853,9 +20237,9 @@ function MetaTitleField({
19853
20237
  borderRadius: 6,
19854
20238
  fontSize: 11,
19855
20239
  fontWeight: 600,
19856
- color: C9.red,
20240
+ color: C10.red,
19857
20241
  backgroundColor: "rgba(239,68,68,0.08)",
19858
- border: `1px solid ${C9.red}`
20242
+ border: `1px solid ${C10.red}`
19859
20243
  },
19860
20244
  children: error
19861
20245
  }
@@ -19864,7 +20248,7 @@ function MetaTitleField({
19864
20248
  }
19865
20249
  );
19866
20250
  }
19867
- var C10 = {
20251
+ var C11 = {
19868
20252
  cyan: "#00E5FF",
19869
20253
  black: "#000",
19870
20254
  green: "#22c55e",
@@ -19879,10 +20263,10 @@ var C10 = {
19879
20263
  var DESC_MIN = 120;
19880
20264
  var DESC_MAX = 160;
19881
20265
  function getCharColor2(len) {
19882
- if (len === 0) return C10.textSecondary;
19883
- if (len >= DESC_MIN && len <= DESC_MAX) return C10.green;
19884
- if (len > 0 && len < DESC_MIN) return C10.orange;
19885
- return C10.red;
20266
+ if (len === 0) return C11.textSecondary;
20267
+ if (len >= DESC_MIN && len <= DESC_MAX) return C11.green;
20268
+ if (len > 0 && len < DESC_MIN) return C11.orange;
20269
+ return C11.red;
19886
20270
  }
19887
20271
  function getProgressPercent2(len) {
19888
20272
  if (len === 0) return 0;
@@ -19890,9 +20274,9 @@ function getProgressPercent2(len) {
19890
20274
  }
19891
20275
  function getProgressColor2(len) {
19892
20276
  if (len === 0) return "var(--theme-elevation-200, #e5e7eb)";
19893
- if (len >= DESC_MIN && len <= DESC_MAX) return C10.green;
19894
- if (len < DESC_MIN) return C10.orange;
19895
- return C10.red;
20277
+ if (len >= DESC_MIN && len <= DESC_MAX) return C11.green;
20278
+ if (len < DESC_MIN) return C11.orange;
20279
+ return C11.red;
19896
20280
  }
19897
20281
  function MetaDescriptionField({
19898
20282
  path,
@@ -19963,7 +20347,7 @@ function MetaDescriptionField({
19963
20347
  style: {
19964
20348
  fontSize: 13,
19965
20349
  fontWeight: 700,
19966
- color: C10.textPrimary
20350
+ color: C11.textPrimary
19967
20351
  },
19968
20352
  children: t.metaDescription.label
19969
20353
  }
@@ -20003,9 +20387,9 @@ function MetaDescriptionField({
20003
20387
  fontSize: 14,
20004
20388
  fontFamily: "inherit",
20005
20389
  borderRadius: 8,
20006
- border: `2px solid ${C10.border}`,
20007
- backgroundColor: C10.surfaceBg,
20008
- color: C10.textPrimary,
20390
+ border: `2px solid ${C11.border}`,
20391
+ backgroundColor: C11.surfaceBg,
20392
+ color: C11.textPrimary,
20009
20393
  outline: "none",
20010
20394
  resize: "vertical",
20011
20395
  lineHeight: 1.5,
@@ -20025,9 +20409,9 @@ function MetaDescriptionField({
20025
20409
  gap: 5,
20026
20410
  padding: "8px 14px",
20027
20411
  borderRadius: 8,
20028
- border: `2px solid ${C10.border}`,
20029
- backgroundColor: loading ? C10.surface50 : C10.cyan,
20030
- color: loading ? C10.textSecondary : C10.black,
20412
+ border: `2px solid ${C11.border}`,
20413
+ backgroundColor: loading ? C11.surface50 : C11.cyan,
20414
+ color: loading ? C11.textSecondary : C11.black,
20031
20415
  fontWeight: 800,
20032
20416
  fontSize: 11,
20033
20417
  textTransform: "uppercase",
@@ -20075,7 +20459,7 @@ function MetaDescriptionField({
20075
20459
  justifyContent: "space-between",
20076
20460
  marginTop: 4,
20077
20461
  fontSize: 10,
20078
- color: C10.textSecondary
20462
+ color: C11.textSecondary
20079
20463
  },
20080
20464
  children: [
20081
20465
  /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
@@ -20109,9 +20493,9 @@ function MetaDescriptionField({
20109
20493
  borderRadius: 6,
20110
20494
  fontSize: 11,
20111
20495
  fontWeight: 600,
20112
- color: C10.red,
20496
+ color: C11.red,
20113
20497
  backgroundColor: "rgba(239,68,68,0.08)",
20114
- border: `1px solid ${C10.red}`
20498
+ border: `1px solid ${C11.red}`
20115
20499
  },
20116
20500
  children: error
20117
20501
  }
@@ -20120,7 +20504,7 @@ function MetaDescriptionField({
20120
20504
  }
20121
20505
  );
20122
20506
  }
20123
- var C11 = {
20507
+ var C12 = {
20124
20508
  cyan: "#00E5FF",
20125
20509
  black: "#000",
20126
20510
  white: "#fff",
@@ -20197,8 +20581,8 @@ function MetaImageField({
20197
20581
  gap: 10,
20198
20582
  padding: "10px 14px",
20199
20583
  borderRadius: 8,
20200
- border: `2px solid ${C11.border}`,
20201
- backgroundColor: C11.surfaceBg,
20584
+ border: `2px solid ${C12.border}`,
20585
+ backgroundColor: C12.surfaceBg,
20202
20586
  boxShadow: "2px 2px 0 0 var(--theme-border-color, rgba(0,0,0,1))"
20203
20587
  },
20204
20588
  children: [
@@ -20217,7 +20601,7 @@ function MetaImageField({
20217
20601
  fontWeight: 900,
20218
20602
  backgroundColor: hasImage ? "rgba(34,197,94,0.15)" : "rgba(255,138,0,0.15)",
20219
20603
  color: hasImage ? "#16a34a" : "#d97706",
20220
- border: `1px solid ${hasImage ? C11.green : C11.orange}`
20604
+ border: `1px solid ${hasImage ? C12.green : C12.orange}`
20221
20605
  },
20222
20606
  children: hasImage ? "\u2713" : "!"
20223
20607
  }
@@ -20229,7 +20613,7 @@ function MetaImageField({
20229
20613
  style: {
20230
20614
  fontSize: 12,
20231
20615
  fontWeight: 700,
20232
- color: C11.textPrimary
20616
+ color: C12.textPrimary
20233
20617
  },
20234
20618
  children: t.metaImage.label
20235
20619
  }
@@ -20239,7 +20623,7 @@ function MetaImageField({
20239
20623
  {
20240
20624
  style: {
20241
20625
  fontSize: 10,
20242
- color: C11.textSecondary,
20626
+ color: C12.textSecondary,
20243
20627
  lineHeight: 1.4
20244
20628
  },
20245
20629
  children: hasImage ? t.metaImage.imageSet : t.metaImage.noImage
@@ -20259,9 +20643,9 @@ function MetaImageField({
20259
20643
  gap: 5,
20260
20644
  padding: "8px 14px",
20261
20645
  borderRadius: 8,
20262
- border: `2px solid ${C11.border}`,
20263
- backgroundColor: loading ? C11.surface50 : success ? C11.green : C11.cyan,
20264
- color: loading ? C11.textSecondary : success ? C11.white : C11.black,
20646
+ border: `2px solid ${C12.border}`,
20647
+ backgroundColor: loading ? C12.surface50 : success ? C12.green : C12.cyan,
20648
+ color: loading ? C12.textSecondary : success ? C12.white : C12.black,
20265
20649
  fontWeight: 800,
20266
20650
  fontSize: 11,
20267
20651
  textTransform: "uppercase",
@@ -20288,9 +20672,9 @@ function MetaImageField({
20288
20672
  borderRadius: 6,
20289
20673
  fontSize: 11,
20290
20674
  fontWeight: 600,
20291
- color: C11.red,
20675
+ color: C12.red,
20292
20676
  backgroundColor: "rgba(239,68,68,0.08)",
20293
- border: `1px solid ${C11.red}`
20677
+ border: `1px solid ${C12.red}`
20294
20678
  },
20295
20679
  children: error
20296
20680
  }
@@ -20299,7 +20683,7 @@ function MetaImageField({
20299
20683
  }
20300
20684
  );
20301
20685
  }
20302
- var C12 = {
20686
+ var C13 = {
20303
20687
  black: "#000",
20304
20688
  white: "#fff",
20305
20689
  green: "#22c55e",
@@ -20313,15 +20697,15 @@ var C12 = {
20313
20697
  function getCompletenessColor(count) {
20314
20698
  switch (count) {
20315
20699
  case 0:
20316
- return C12.red;
20700
+ return C13.red;
20317
20701
  case 1:
20318
- return C12.orange;
20702
+ return C13.orange;
20319
20703
  case 2:
20320
- return C12.yellow;
20704
+ return C13.yellow;
20321
20705
  case 3:
20322
- return C12.green;
20706
+ return C13.green;
20323
20707
  default:
20324
- return C12.textSecondary;
20708
+ return C13.textSecondary;
20325
20709
  }
20326
20710
  }
20327
20711
  function getCompletenessLabel(count, ov) {
@@ -20379,8 +20763,8 @@ function OverviewField({
20379
20763
  fontFamily: "var(--font-body, Inter, system-ui, sans-serif)",
20380
20764
  padding: "12px 14px",
20381
20765
  borderRadius: 10,
20382
- border: `2px solid ${C12.border}`,
20383
- backgroundColor: C12.surfaceBg,
20766
+ border: `2px solid ${C13.border}`,
20767
+ backgroundColor: C13.surfaceBg,
20384
20768
  boxShadow: "3px 3px 0 0 var(--theme-border-color, rgba(0,0,0,1))",
20385
20769
  marginBottom: 12
20386
20770
  },
@@ -20403,7 +20787,7 @@ function OverviewField({
20403
20787
  fontWeight: 800,
20404
20788
  textTransform: "uppercase",
20405
20789
  letterSpacing: "0.04em",
20406
- color: C12.textPrimary
20790
+ color: C13.textPrimary
20407
20791
  },
20408
20792
  children: t.overview.metaCompleteness
20409
20793
  }
@@ -20418,8 +20802,8 @@ function OverviewField({
20418
20802
  fontSize: 11,
20419
20803
  fontWeight: 800,
20420
20804
  backgroundColor: completenessColor,
20421
- color: completenessColor === C12.yellow ? C12.black : C12.white,
20422
- border: `2px solid ${C12.border}`,
20805
+ color: completenessColor === C13.yellow ? C13.black : C13.white,
20806
+ border: `2px solid ${C13.border}`,
20423
20807
  textTransform: "uppercase",
20424
20808
  letterSpacing: "0.03em"
20425
20809
  },
@@ -20517,7 +20901,7 @@ function OverviewField({
20517
20901
  style: {
20518
20902
  fontSize: 12,
20519
20903
  fontWeight: 600,
20520
- color: item.filled ? C12.textPrimary : C12.textSecondary
20904
+ color: item.filled ? C13.textPrimary : C13.textSecondary
20521
20905
  },
20522
20906
  children: item.label
20523
20907
  }
@@ -20529,7 +20913,7 @@ function OverviewField({
20529
20913
  marginLeft: "auto",
20530
20914
  fontSize: 10,
20531
20915
  fontWeight: 700,
20532
- color: item.filled ? C12.green : C12.red,
20916
+ color: item.filled ? C13.green : C13.red,
20533
20917
  textTransform: "uppercase",
20534
20918
  letterSpacing: "0.03em"
20535
20919
  },
@@ -20546,7 +20930,7 @@ function OverviewField({
20546
20930
  }
20547
20931
  );
20548
20932
  }
20549
- var C13 = {
20933
+ var C14 = {
20550
20934
  cyan: "#00E5FF",
20551
20935
  black: "#000",
20552
20936
  white: "#fff",
@@ -20565,10 +20949,10 @@ var G = {
20565
20949
  descGrey: "#4d5156",
20566
20950
  faviconBg: "#e8eaed"};
20567
20951
  function charCountColor2(len, min, max) {
20568
- if (len >= min && len <= max) return C13.green;
20569
- if (len > 0 && len < min) return C13.orange;
20570
- if (len > max) return C13.red;
20571
- return C13.textSecondary;
20952
+ if (len >= min && len <= max) return C14.green;
20953
+ if (len > 0 && len < min) return C14.orange;
20954
+ if (len > max) return C14.red;
20955
+ return C14.textSecondary;
20572
20956
  }
20573
20957
  function truncateText(text, maxChars) {
20574
20958
  if (text.length <= maxChars) return text;
@@ -20623,8 +21007,8 @@ function SerpPreview({
20623
21007
  padding: "10px 12px",
20624
21008
  cursor: "pointer",
20625
21009
  borderRadius: 8,
20626
- border: `2px solid ${C13.border}`,
20627
- backgroundColor: C13.surface50,
21010
+ border: `2px solid ${C14.border}`,
21011
+ backgroundColor: C14.surface50,
20628
21012
  userSelect: "none"
20629
21013
  },
20630
21014
  children: [
@@ -20639,7 +21023,7 @@ function SerpPreview({
20639
21023
  fontWeight: 800,
20640
21024
  textTransform: "uppercase",
20641
21025
  letterSpacing: "0.04em",
20642
- color: C13.textPrimary
21026
+ color: C14.textPrimary
20643
21027
  },
20644
21028
  children: [
20645
21029
  /* @__PURE__ */ jsxRuntime.jsxs(
@@ -20671,7 +21055,7 @@ function SerpPreview({
20671
21055
  transition: "transform 0.2s",
20672
21056
  display: "inline-block",
20673
21057
  transform: open ? "rotate(90deg)" : "none",
20674
- color: C13.textSecondary
21058
+ color: C14.textSecondary
20675
21059
  },
20676
21060
  children: "\u25B6"
20677
21061
  }
@@ -20704,10 +21088,10 @@ function SerpPreview({
20704
21088
  "div",
20705
21089
  {
20706
21090
  style: {
20707
- backgroundColor: C13.white,
20708
- border: `2px solid ${C13.border}`,
21091
+ backgroundColor: C14.white,
21092
+ border: `2px solid ${C14.border}`,
20709
21093
  borderRadius: 12,
20710
- boxShadow: `3px 3px 0 0 ${C13.border}`,
21094
+ boxShadow: `3px 3px 0 0 ${C14.border}`,
20711
21095
  padding: isDesktop ? 20 : 14,
20712
21096
  maxWidth: isDesktop ? 650 : 380,
20713
21097
  overflow: "hidden"
@@ -20770,7 +21154,7 @@ function SerpPreview({
20770
21154
  style: {
20771
21155
  fontSize: 14,
20772
21156
  fontWeight: 400,
20773
- color: C13.black,
21157
+ color: C14.black,
20774
21158
  lineHeight: 1.3,
20775
21159
  whiteSpace: "nowrap",
20776
21160
  overflow: "hidden",
@@ -20834,7 +21218,7 @@ function SerpPreview({
20834
21218
  "span",
20835
21219
  {
20836
21220
  style: {
20837
- color: C13.textSecondary,
21221
+ color: C14.textSecondary,
20838
21222
  fontStyle: "italic",
20839
21223
  fontSize: titleFontSize - 2
20840
21224
  },
@@ -20863,7 +21247,7 @@ function SerpPreview({
20863
21247
  "span",
20864
21248
  {
20865
21249
  style: {
20866
- color: C13.textSecondary,
21250
+ color: C14.textSecondary,
20867
21251
  fontStyle: "italic",
20868
21252
  fontSize: descFontSize - 1
20869
21253
  },
@@ -20896,7 +21280,7 @@ function SerpPreview({
20896
21280
  fontSize: 11
20897
21281
  },
20898
21282
  children: [
20899
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: C13.textSecondary, fontWeight: 600 }, children: t.serpPreview.previewTitle }),
21283
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: C14.textSecondary, fontWeight: 600 }, children: t.serpPreview.previewTitle }),
20900
21284
  /* @__PURE__ */ jsxRuntime.jsxs(
20901
21285
  "span",
20902
21286
  {
@@ -20925,7 +21309,7 @@ function SerpPreview({
20925
21309
  fontSize: 11
20926
21310
  },
20927
21311
  children: [
20928
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: C13.textSecondary, fontWeight: 600 }, children: t.serpPreview.previewDescription }),
21312
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: C14.textSecondary, fontWeight: 600 }, children: t.serpPreview.previewDescription }),
20929
21313
  /* @__PURE__ */ jsxRuntime.jsxs(
20930
21314
  "span",
20931
21315
  {
@@ -20954,14 +21338,14 @@ function SerpPreview({
20954
21338
  fontSize: 11
20955
21339
  },
20956
21340
  children: [
20957
- /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: C13.textSecondary, fontWeight: 600 }, children: t.serpPreview.url }),
21341
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { color: C14.textSecondary, fontWeight: 600 }, children: t.serpPreview.url }),
20958
21342
  /* @__PURE__ */ jsxRuntime.jsxs(
20959
21343
  "span",
20960
21344
  {
20961
21345
  style: {
20962
21346
  fontWeight: 700,
20963
21347
  fontVariantNumeric: "tabular-nums",
20964
- color: fullUrl.length <= 75 ? C13.green : C13.red
21348
+ color: fullUrl.length <= 75 ? C14.green : C14.red
20965
21349
  },
20966
21350
  children: [
20967
21351
  fullUrl.length,
@@ -20996,13 +21380,13 @@ function DeviceButton({
20996
21380
  gap: 5,
20997
21381
  padding: "4px 12px",
20998
21382
  borderRadius: 6,
20999
- border: `2px solid ${C13.border}`,
21383
+ border: `2px solid ${C14.border}`,
21000
21384
  fontSize: 11,
21001
21385
  fontWeight: 700,
21002
21386
  cursor: "pointer",
21003
- backgroundColor: active ? C13.cyan : C13.surfaceBg,
21004
- color: active ? C13.black : C13.textPrimary,
21005
- boxShadow: active ? `2px 2px 0 0 ${C13.border}` : "none",
21387
+ backgroundColor: active ? C14.cyan : C14.surfaceBg,
21388
+ color: active ? C14.black : C14.textPrimary,
21389
+ boxShadow: active ? `2px 2px 0 0 ${C14.border}` : "none",
21006
21390
  transition: "background-color 0.15s"
21007
21391
  },
21008
21392
  children: [