@focus-reactive/payload-plugin-seo 1.6.1 → 1.7.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/README.md +12 -1
- package/dist/admin.css +200 -17
- package/dist/components/SeoButton/SeoButtonInner.d.ts.map +1 -1
- package/dist/components/SeoButton/SeoButtonInner.js +46 -16
- package/dist/components/SeoButton/SeoButtonInner.js.map +1 -1
- package/dist/components/SeoDrawer/build-analysis-input.d.ts +2 -2
- package/dist/components/SeoDrawer/build-analysis-input.d.ts.map +1 -1
- package/dist/components/SeoDrawer/build-analysis-input.js +1 -1
- package/dist/components/SeoDrawer/build-analysis-input.js.map +1 -1
- package/dist/components/SeoDrawer/buildInput.d.ts +3 -3
- package/dist/components/SeoDrawer/buildInput.d.ts.map +1 -1
- package/dist/components/SeoDrawer/buildInput.js +3 -2
- package/dist/components/SeoDrawer/buildInput.js.map +1 -1
- package/dist/components/SeoDrawer/components/SerpPreview/highlight-keyphrase.d.ts +1 -1
- package/dist/components/SeoDrawer/components/SerpPreview/highlight-keyphrase.d.ts.map +1 -1
- package/dist/components/SeoDrawer/components/SerpPreview/highlight-keyphrase.js +10 -3
- package/dist/components/SeoDrawer/components/SerpPreview/highlight-keyphrase.js.map +1 -1
- package/dist/components/SeoDrawer/components/SerpPreview/index.d.ts +2 -1
- package/dist/components/SeoDrawer/components/SerpPreview/index.d.ts.map +1 -1
- package/dist/components/SeoDrawer/components/SerpPreview/index.js +8 -2
- package/dist/components/SeoDrawer/components/SerpPreview/index.js.map +1 -1
- package/dist/components/SeoDrawer/components/SerpPreview/variants.d.ts +6 -6
- package/dist/components/SeoDrawer/components/TabWrapper.d.ts +6 -0
- package/dist/components/SeoDrawer/components/TabWrapper.d.ts.map +1 -0
- package/dist/components/SeoDrawer/components/TabWrapper.js +8 -0
- package/dist/components/SeoDrawer/components/TabWrapper.js.map +1 -0
- package/dist/components/SeoDrawer/index.d.ts +11 -4
- package/dist/components/SeoDrawer/index.d.ts.map +1 -1
- package/dist/components/SeoDrawer/index.js +38 -16
- package/dist/components/SeoDrawer/index.js.map +1 -1
- package/dist/components/SeoDrawer/keyphraseState.d.ts +16 -0
- package/dist/components/SeoDrawer/keyphraseState.d.ts.map +1 -0
- package/dist/components/SeoDrawer/keyphraseState.js +73 -0
- package/dist/components/SeoDrawer/keyphraseState.js.map +1 -0
- package/dist/components/SeoDrawer/keyphraseStorage.d.ts +5 -0
- package/dist/components/SeoDrawer/keyphraseStorage.d.ts.map +1 -0
- package/dist/components/SeoDrawer/keyphraseStorage.js +60 -0
- package/dist/components/SeoDrawer/keyphraseStorage.js.map +1 -0
- package/dist/components/SeoDrawer/tabs/InclusiveTab.d.ts.map +1 -1
- package/dist/components/SeoDrawer/tabs/InclusiveTab.js +3 -2
- package/dist/components/SeoDrawer/tabs/InclusiveTab.js.map +1 -1
- package/dist/components/SeoDrawer/tabs/KeyphraseTab.d.ts +14 -7
- package/dist/components/SeoDrawer/tabs/KeyphraseTab.d.ts.map +1 -1
- package/dist/components/SeoDrawer/tabs/KeyphraseTab.js +110 -69
- package/dist/components/SeoDrawer/tabs/KeyphraseTab.js.map +1 -1
- package/dist/components/SeoDrawer/tabs/OnPageTab.d.ts.map +1 -1
- package/dist/components/SeoDrawer/tabs/OnPageTab.js +3 -2
- package/dist/components/SeoDrawer/tabs/OnPageTab.js.map +1 -1
- package/dist/components/SeoDrawer/tabs/ReadabilityTab.d.ts.map +1 -1
- package/dist/components/SeoDrawer/tabs/ReadabilityTab.js +3 -2
- package/dist/components/SeoDrawer/tabs/ReadabilityTab.js.map +1 -1
- package/dist/components/SeoDrawer/tabs/SerpTab.d.ts +2 -1
- package/dist/components/SeoDrawer/tabs/SerpTab.d.ts.map +1 -1
- package/dist/components/SeoDrawer/tabs/SerpTab.js +14 -3
- package/dist/components/SeoDrawer/tabs/SerpTab.js.map +1 -1
- package/dist/components/SeoDrawer/tabs/VitalsTab.d.ts.map +1 -1
- package/dist/components/SeoDrawer/tabs/VitalsTab.js +7 -3
- package/dist/components/SeoDrawer/tabs/VitalsTab.js.map +1 -1
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseCard.d.ts +21 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseCard.d.ts.map +1 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseCard.js +38 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseCard.js.map +1 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseDetail.d.ts +25 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseDetail.d.ts.map +1 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseDetail.js +127 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseDetail.js.map +1 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseRail.d.ts +12 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseRail.d.ts.map +1 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseRail.js +54 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/KeyphraseRail.js.map +1 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/SynonymsField.d.ts +7 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/SynonymsField.d.ts.map +1 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/SynonymsField.js +93 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/SynonymsField.js.map +1 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/icons.d.ts +2 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/icons.d.ts.map +1 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/icons.js +7 -0
- package/dist/components/SeoDrawer/tabs/keyphrase/icons.js.map +1 -0
- package/dist/components/SeoDrawer/useAnalysis.d.ts +0 -1
- package/dist/components/SeoDrawer/useAnalysis.d.ts.map +1 -1
- package/dist/components/SeoDrawer/useAnalysis.js +0 -3
- package/dist/components/SeoDrawer/useAnalysis.js.map +1 -1
- package/dist/components/SeoDrawer/useKeyphrases.d.ts +18 -0
- package/dist/components/SeoDrawer/useKeyphrases.d.ts.map +1 -0
- package/dist/components/SeoDrawer/useKeyphrases.js +78 -0
- package/dist/components/SeoDrawer/useKeyphrases.js.map +1 -0
- package/dist/components/SeoDrawer/useLiveDocument.d.ts +4 -3
- package/dist/components/SeoDrawer/useLiveDocument.d.ts.map +1 -1
- package/dist/components/SeoDrawer/useLiveDocument.js +17 -9
- package/dist/components/SeoDrawer/useLiveDocument.js.map +1 -1
- package/dist/components/SeoDrawer/variants.d.ts +1 -1
- package/dist/components/SeoDrawer/variants.d.ts.map +1 -1
- package/dist/components/SeoDrawer/variants.js +2 -1
- package/dist/components/SeoDrawer/variants.js.map +1 -1
- package/dist/constants/index.d.ts +1 -0
- package/dist/constants/index.d.ts.map +1 -1
- package/dist/constants/index.js +2 -0
- package/dist/constants/index.js.map +1 -1
- package/dist/engine/buildPaper.d.ts +2 -2
- package/dist/engine/buildPaper.d.ts.map +1 -1
- package/dist/engine/buildPaper.js +4 -2
- package/dist/engine/buildPaper.js.map +1 -1
- package/dist/engine/runAnalysis/index.d.ts.map +1 -1
- package/dist/engine/runAnalysis/index.js +7 -0
- package/dist/engine/runAnalysis/index.js.map +1 -1
- package/dist/engine/runAnalysis/services/derive-related.d.ts +4 -0
- package/dist/engine/runAnalysis/services/derive-related.d.ts.map +1 -0
- package/dist/engine/runAnalysis/services/derive-related.js +16 -0
- package/dist/engine/runAnalysis/services/derive-related.js.map +1 -0
- package/dist/engine/types/analysis.d.ts +11 -0
- package/dist/engine/types/analysis.d.ts.map +1 -1
- package/dist/ui/Button.d.ts +9 -0
- package/dist/ui/Button.d.ts.map +1 -0
- package/dist/ui/Button.js +34 -0
- package/dist/ui/Button.js.map +1 -0
- package/dist/ui/ScoreRing.d.ts +17 -4
- package/dist/ui/ScoreRing.d.ts.map +1 -1
- package/dist/ui/ScoreRing.js +38 -14
- package/dist/ui/ScoreRing.js.map +1 -1
- package/package.json +1 -1
- package/dist/components/SeoDrawer/keyphrasePending.d.ts +0 -2
- package/dist/components/SeoDrawer/keyphrasePending.d.ts.map +0 -1
- package/dist/components/SeoDrawer/keyphrasePending.js +0 -9
- package/dist/components/SeoDrawer/keyphrasePending.js.map +0 -1
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { MAX_KEYPHRASES } from "../../constants";
|
|
2
|
+
function makeId() {
|
|
3
|
+
const c = globalThis.crypto;
|
|
4
|
+
return c?.randomUUID ? c.randomUUID() : `kp_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;
|
|
5
|
+
}
|
|
6
|
+
function createEntry(text = "", synonyms = []) {
|
|
7
|
+
return {
|
|
8
|
+
id: makeId(),
|
|
9
|
+
text,
|
|
10
|
+
synonyms: [...synonyms]
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function firstEmptyId(list) {
|
|
14
|
+
return list.find((k) => k.text.trim() === "")?.id;
|
|
15
|
+
}
|
|
16
|
+
function addRelated(list) {
|
|
17
|
+
if (list.length >= MAX_KEYPHRASES)
|
|
18
|
+
return list;
|
|
19
|
+
if (firstEmptyId(list))
|
|
20
|
+
return list;
|
|
21
|
+
return [...list, createEntry()];
|
|
22
|
+
}
|
|
23
|
+
function updateText(list, id, text) {
|
|
24
|
+
return list.map((k) => k.id === id ? { ...k, text } : k);
|
|
25
|
+
}
|
|
26
|
+
function addSynonym(list, id, syn) {
|
|
27
|
+
const value = syn.trim();
|
|
28
|
+
if (!value)
|
|
29
|
+
return list;
|
|
30
|
+
return list.map((k) => {
|
|
31
|
+
if (k.id !== id)
|
|
32
|
+
return k;
|
|
33
|
+
if (k.synonyms.some((s) => s.toLowerCase() === value.toLowerCase()))
|
|
34
|
+
return k;
|
|
35
|
+
return { ...k, synonyms: [...k.synonyms, value] };
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
function removeSynonym(list, id, index) {
|
|
39
|
+
return list.map(
|
|
40
|
+
(k) => k.id === id ? { ...k, synonyms: k.synonyms.filter((_, i) => i !== index) } : k
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
function remove(list, id) {
|
|
44
|
+
return list.filter((k) => k.id !== id);
|
|
45
|
+
}
|
|
46
|
+
function setFocus(list, id) {
|
|
47
|
+
const target = list.find((k) => k.id === id);
|
|
48
|
+
if (!target)
|
|
49
|
+
return list;
|
|
50
|
+
return [target, ...list.filter((k) => k.id !== id)];
|
|
51
|
+
}
|
|
52
|
+
function isDuplicate(list, id, text) {
|
|
53
|
+
const value = text.trim().toLowerCase();
|
|
54
|
+
if (!value)
|
|
55
|
+
return false;
|
|
56
|
+
return list.some((k) => k.id !== id && k.text.trim().toLowerCase() === value);
|
|
57
|
+
}
|
|
58
|
+
function pruneEmpties(list) {
|
|
59
|
+
return list.filter((k) => k.text.trim() !== "");
|
|
60
|
+
}
|
|
61
|
+
export {
|
|
62
|
+
addRelated,
|
|
63
|
+
addSynonym,
|
|
64
|
+
createEntry,
|
|
65
|
+
firstEmptyId,
|
|
66
|
+
isDuplicate,
|
|
67
|
+
pruneEmpties,
|
|
68
|
+
remove,
|
|
69
|
+
removeSynonym,
|
|
70
|
+
setFocus,
|
|
71
|
+
updateText
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=keyphraseState.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/SeoDrawer/keyphraseState.ts"],"sourcesContent":["import { MAX_KEYPHRASES } from \"../../constants\";\n\nexport interface KeyphraseEntry {\n id: string;\n text: string;\n synonyms: string[];\n}\n\nfunction makeId(): string {\n const c = (globalThis as { crypto?: { randomUUID?: () => string } }).crypto;\n\n return c?.randomUUID\n ? c.randomUUID()\n : `kp_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n}\n\nexport function createEntry(text = \"\", synonyms: string[] = []): KeyphraseEntry {\n return {\n id: makeId(),\n text,\n synonyms: [...synonyms],\n };\n}\n\nexport function firstEmptyId(list: KeyphraseEntry[]): string | undefined {\n return list.find((k) => k.text.trim() === \"\")?.id;\n}\n\nexport function addRelated(list: KeyphraseEntry[]): KeyphraseEntry[] {\n if (list.length >= MAX_KEYPHRASES) return list;\n if (firstEmptyId(list)) return list;\n\n return [...list, createEntry()];\n}\n\nexport function updateText(list: KeyphraseEntry[], id: string, text: string): KeyphraseEntry[] {\n return list.map((k) => (k.id === id ? { ...k, text } : k));\n}\n\nexport function addSynonym(list: KeyphraseEntry[], id: string, syn: string): KeyphraseEntry[] {\n const value = syn.trim();\n if (!value) return list;\n\n return list.map((k) => {\n if (k.id !== id) return k;\n if (k.synonyms.some((s) => s.toLowerCase() === value.toLowerCase())) return k;\n return { ...k, synonyms: [...k.synonyms, value] };\n });\n}\n\nexport function removeSynonym(list: KeyphraseEntry[], id: string, index: number): KeyphraseEntry[] {\n return list.map((k) =>\n k.id === id ? { ...k, synonyms: k.synonyms.filter((_, i) => i !== index) } : k\n );\n}\n\nexport function remove(list: KeyphraseEntry[], id: string): KeyphraseEntry[] {\n return list.filter((k) => k.id !== id);\n}\n\nexport function setFocus(list: KeyphraseEntry[], id: string): KeyphraseEntry[] {\n const target = list.find((k) => k.id === id);\n if (!target) return list;\n\n return [target, ...list.filter((k) => k.id !== id)];\n}\n\nexport function isDuplicate(list: KeyphraseEntry[], id: string, text: string): boolean {\n const value = text.trim().toLowerCase();\n if (!value) return false;\n\n return list.some((k) => k.id !== id && k.text.trim().toLowerCase() === value);\n}\n\nexport function pruneEmpties(list: KeyphraseEntry[]): KeyphraseEntry[] {\n return list.filter((k) => k.text.trim() !== \"\");\n}\n"],"mappings":"AAAA,SAAS,sBAAsB;AAQ/B,SAAS,SAAiB;AACxB,QAAM,IAAK,WAA0D;AAErE,SAAO,GAAG,aACN,EAAE,WAAW,IACb,MAAM,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC,GAAG,KAAK,IAAI,EAAE,SAAS,EAAE,CAAC;AACzE;AAEO,SAAS,YAAY,OAAO,IAAI,WAAqB,CAAC,GAAmB;AAC9E,SAAO;AAAA,IACL,IAAI,OAAO;AAAA,IACX;AAAA,IACA,UAAU,CAAC,GAAG,QAAQ;AAAA,EACxB;AACF;AAEO,SAAS,aAAa,MAA4C;AACvE,SAAO,KAAK,KAAK,CAAC,MAAM,EAAE,KAAK,KAAK,MAAM,EAAE,GAAG;AACjD;AAEO,SAAS,WAAW,MAA0C;AACnE,MAAI,KAAK,UAAU;AAAgB,WAAO;AAC1C,MAAI,aAAa,IAAI;AAAG,WAAO;AAE/B,SAAO,CAAC,GAAG,MAAM,YAAY,CAAC;AAChC;AAEO,SAAS,WAAW,MAAwB,IAAY,MAAgC;AAC7F,SAAO,KAAK,IAAI,CAAC,MAAO,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,CAAE;AAC3D;AAEO,SAAS,WAAW,MAAwB,IAAY,KAA+B;AAC5F,QAAM,QAAQ,IAAI,KAAK;AACvB,MAAI,CAAC;AAAO,WAAO;AAEnB,SAAO,KAAK,IAAI,CAAC,MAAM;AACrB,QAAI,EAAE,OAAO;AAAI,aAAO;AACxB,QAAI,EAAE,SAAS,KAAK,CAAC,MAAM,EAAE,YAAY,MAAM,MAAM,YAAY,CAAC;AAAG,aAAO;AAC5E,WAAO,EAAE,GAAG,GAAG,UAAU,CAAC,GAAG,EAAE,UAAU,KAAK,EAAE;AAAA,EAClD,CAAC;AACH;AAEO,SAAS,cAAc,MAAwB,IAAY,OAAiC;AACjG,SAAO,KAAK;AAAA,IAAI,CAAC,MACf,EAAE,OAAO,KAAK,EAAE,GAAG,GAAG,UAAU,EAAE,SAAS,OAAO,CAAC,GAAG,MAAM,MAAM,KAAK,EAAE,IAAI;AAAA,EAC/E;AACF;AAEO,SAAS,OAAO,MAAwB,IAA8B;AAC3E,SAAO,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE;AACvC;AAEO,SAAS,SAAS,MAAwB,IAA8B;AAC7E,QAAM,SAAS,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AAC3C,MAAI,CAAC;AAAQ,WAAO;AAEpB,SAAO,CAAC,QAAQ,GAAG,KAAK,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC;AACpD;AAEO,SAAS,YAAY,MAAwB,IAAY,MAAuB;AACrF,QAAM,QAAQ,KAAK,KAAK,EAAE,YAAY;AACtC,MAAI,CAAC;AAAO,WAAO;AAEnB,SAAO,KAAK,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE,KAAK,KAAK,EAAE,YAAY,MAAM,KAAK;AAC9E;AAEO,SAAS,aAAa,MAA0C;AACrE,SAAO,KAAK,OAAO,CAAC,MAAM,EAAE,KAAK,KAAK,MAAM,EAAE;AAChD;","names":[]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { KeyphraseEntry } from './keyphraseState';
|
|
2
|
+
export declare function storageKey(collectionSlug: string, docId: string, locale: string): string;
|
|
3
|
+
export declare function loadKeyphrases(key: string): KeyphraseEntry[];
|
|
4
|
+
export declare function saveKeyphrases(key: string, list: KeyphraseEntry[]): void;
|
|
5
|
+
//# sourceMappingURL=keyphraseStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"keyphraseStorage.d.ts","sourceRoot":"","sources":["../../../src/components/SeoDrawer/keyphraseStorage.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AASvD,wBAAgB,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAExF;AAWD,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAyB5D;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,IAAI,CAmBxE"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { createEntry, pruneEmpties } from "./keyphraseState";
|
|
2
|
+
const PREFIX = "seo-kw";
|
|
3
|
+
function storageKey(collectionSlug, docId, locale) {
|
|
4
|
+
return `${PREFIX}:${collectionSlug}:${docId}:${locale}`;
|
|
5
|
+
}
|
|
6
|
+
function store() {
|
|
7
|
+
try {
|
|
8
|
+
if (typeof window === "undefined" || !window.localStorage)
|
|
9
|
+
return null;
|
|
10
|
+
return window.localStorage;
|
|
11
|
+
} catch {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
function loadKeyphrases(key) {
|
|
16
|
+
const fallback = () => [createEntry()];
|
|
17
|
+
const s = store();
|
|
18
|
+
if (!s)
|
|
19
|
+
return fallback();
|
|
20
|
+
try {
|
|
21
|
+
const raw = s.getItem(key);
|
|
22
|
+
if (!raw)
|
|
23
|
+
return fallback();
|
|
24
|
+
const parsed = JSON.parse(raw);
|
|
25
|
+
if (!Array.isArray(parsed) || parsed.length === 0)
|
|
26
|
+
return fallback();
|
|
27
|
+
const list = parsed.filter((e) => e && typeof e.text === "string").map(
|
|
28
|
+
(e) => createEntry(
|
|
29
|
+
e.text,
|
|
30
|
+
Array.isArray(e.synonyms) ? e.synonyms.filter((x) => typeof x === "string") : []
|
|
31
|
+
)
|
|
32
|
+
);
|
|
33
|
+
return list.length ? list : fallback();
|
|
34
|
+
} catch {
|
|
35
|
+
return fallback();
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function saveKeyphrases(key, list) {
|
|
39
|
+
const s = store();
|
|
40
|
+
if (!s)
|
|
41
|
+
return;
|
|
42
|
+
try {
|
|
43
|
+
const persistable = pruneEmpties(list).map((k) => ({
|
|
44
|
+
text: k.text,
|
|
45
|
+
synonyms: k.synonyms
|
|
46
|
+
}));
|
|
47
|
+
if (persistable.length === 0) {
|
|
48
|
+
s.removeItem(key);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
s.setItem(key, JSON.stringify(persistable));
|
|
52
|
+
} catch {
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
loadKeyphrases,
|
|
57
|
+
saveKeyphrases,
|
|
58
|
+
storageKey
|
|
59
|
+
};
|
|
60
|
+
//# sourceMappingURL=keyphraseStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../../src/components/SeoDrawer/keyphraseStorage.ts"],"sourcesContent":["import { createEntry, pruneEmpties } from './keyphraseState';\nimport type { KeyphraseEntry } from './keyphraseState';\n\nconst PREFIX = \"seo-kw\";\n\ninterface StoredEntry {\n text: string;\n synonyms: string[];\n}\n\nexport function storageKey(collectionSlug: string, docId: string, locale: string): string {\n return `${PREFIX}:${collectionSlug}:${docId}:${locale}`;\n}\n\nfunction store(): Storage | null {\n try {\n if (typeof window === \"undefined\" || !window.localStorage) return null;\n return window.localStorage;\n } catch {\n return null;\n }\n}\n\nexport function loadKeyphrases(key: string): KeyphraseEntry[] {\n const fallback = () => [createEntry()];\n const s = store();\n if (!s) return fallback();\n\n try {\n const raw = s.getItem(key);\n if (!raw) return fallback();\n\n const parsed = JSON.parse(raw) as StoredEntry[];\n if (!Array.isArray(parsed) || parsed.length === 0) return fallback();\n\n const list = parsed\n .filter((e) => e && typeof e.text === \"string\")\n .map((e) =>\n createEntry(\n e.text,\n Array.isArray(e.synonyms) ? e.synonyms.filter((x) => typeof x === \"string\") : []\n )\n );\n\n return list.length ? list : fallback();\n } catch {\n return fallback();\n }\n}\n\nexport function saveKeyphrases(key: string, list: KeyphraseEntry[]): void {\n const s = store();\n if (!s) return;\n\n try {\n const persistable = pruneEmpties(list).map<StoredEntry>((k) => ({\n text: k.text,\n synonyms: k.synonyms,\n }));\n\n if (persistable.length === 0) {\n s.removeItem(key);\n return;\n }\n\n s.setItem(key, JSON.stringify(persistable));\n } catch {\n // quota / serialization — fail soft\n }\n}\n"],"mappings":"AAAA,SAAS,aAAa,oBAAoB;AAG1C,MAAM,SAAS;AAOR,SAAS,WAAW,gBAAwB,OAAe,QAAwB;AACxF,SAAO,GAAG,MAAM,IAAI,cAAc,IAAI,KAAK,IAAI,MAAM;AACvD;AAEA,SAAS,QAAwB;AAC/B,MAAI;AACF,QAAI,OAAO,WAAW,eAAe,CAAC,OAAO;AAAc,aAAO;AAClE,WAAO,OAAO;AAAA,EAChB,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,eAAe,KAA+B;AAC5D,QAAM,WAAW,MAAM,CAAC,YAAY,CAAC;AACrC,QAAM,IAAI,MAAM;AAChB,MAAI,CAAC;AAAG,WAAO,SAAS;AAExB,MAAI;AACF,UAAM,MAAM,EAAE,QAAQ,GAAG;AACzB,QAAI,CAAC;AAAK,aAAO,SAAS;AAE1B,UAAM,SAAS,KAAK,MAAM,GAAG;AAC7B,QAAI,CAAC,MAAM,QAAQ,MAAM,KAAK,OAAO,WAAW;AAAG,aAAO,SAAS;AAEnE,UAAM,OAAO,OACV,OAAO,CAAC,MAAM,KAAK,OAAO,EAAE,SAAS,QAAQ,EAC7C;AAAA,MAAI,CAAC,MACJ;AAAA,QACE,EAAE;AAAA,QACF,MAAM,QAAQ,EAAE,QAAQ,IAAI,EAAE,SAAS,OAAO,CAAC,MAAM,OAAO,MAAM,QAAQ,IAAI,CAAC;AAAA,MACjF;AAAA,IACF;AAEF,WAAO,KAAK,SAAS,OAAO,SAAS;AAAA,EACvC,QAAQ;AACN,WAAO,SAAS;AAAA,EAClB;AACF;AAEO,SAAS,eAAe,KAAa,MAA8B;AACxE,QAAM,IAAI,MAAM;AAChB,MAAI,CAAC;AAAG;AAER,MAAI;AACF,UAAM,cAAc,aAAa,IAAI,EAAE,IAAiB,CAAC,OAAO;AAAA,MAC9D,MAAM,EAAE;AAAA,MACR,UAAU,EAAE;AAAA,IACd,EAAE;AAEF,QAAI,YAAY,WAAW,GAAG;AAC5B,QAAE,WAAW,GAAG;AAChB;AAAA,IACF;AAEA,MAAE,QAAQ,KAAK,KAAK,UAAU,WAAW,CAAC;AAAA,EAC5C,QAAQ;AAAA,EAER;AACF;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InclusiveTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/InclusiveTab.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"InclusiveTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/InclusiveTab.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAOrE,wBAAgB,YAAY,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,cAAc,CAAC,WAAW,CAAC,CAAA;CAAE,2CAgE3E"}
|
|
@@ -4,9 +4,10 @@ import { cn, ROW_SEPARATOR } from "../../../utils/style";
|
|
|
4
4
|
import { TabHeader } from "../../../ui/TabHeader";
|
|
5
5
|
import { SectionCard } from "../../../ui/SectionCard";
|
|
6
6
|
import { Pill } from "../../../ui/Pill";
|
|
7
|
+
import { TabWrapper } from "../components/TabWrapper";
|
|
7
8
|
function InclusiveTab({ data }) {
|
|
8
9
|
const flagged = data.categories.reduce((n, c) => n + c.flags.length, 0);
|
|
9
|
-
return /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-[13px]", children: [
|
|
10
|
+
return /* @__PURE__ */ jsx(TabWrapper, { children: /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-[13px]", children: [
|
|
10
11
|
/* @__PURE__ */ jsx(
|
|
11
12
|
TabHeader,
|
|
12
13
|
{
|
|
@@ -59,7 +60,7 @@ function InclusiveTab({ data }) {
|
|
|
59
60
|
)) })
|
|
60
61
|
}
|
|
61
62
|
)
|
|
62
|
-
] });
|
|
63
|
+
] }) });
|
|
63
64
|
}
|
|
64
65
|
export {
|
|
65
66
|
InclusiveTab
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/SeoDrawer/tabs/InclusiveTab.tsx"],"sourcesContent":["\"use client\";\n\nimport type { AnalysisResult } from \"../../../engine/types/analysis\";\nimport { cn, ROW_SEPARATOR } from \"../../../utils/style\";\nimport { TabHeader } from \"../../../ui/TabHeader\";\nimport { SectionCard } from \"../../../ui/SectionCard\";\nimport { Pill } from \"../../../ui/Pill\";\n\nexport function InclusiveTab({ data }: { data: AnalysisResult[\"inclusive\"] }) {\n const flagged = data.categories.reduce((n, c) => n + c.flags.length, 0);\n\n return (\n <section className=\"flex flex-col gap-[13px]\">\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/SeoDrawer/tabs/InclusiveTab.tsx"],"sourcesContent":["\"use client\";\n\nimport type { AnalysisResult } from \"../../../engine/types/analysis\";\nimport { cn, ROW_SEPARATOR } from \"../../../utils/style\";\nimport { TabHeader } from \"../../../ui/TabHeader\";\nimport { SectionCard } from \"../../../ui/SectionCard\";\nimport { Pill } from \"../../../ui/Pill\";\nimport { TabWrapper } from \"../components/TabWrapper\";\n\nexport function InclusiveTab({ data }: { data: AnalysisResult[\"inclusive\"] }) {\n const flagged = data.categories.reduce((n, c) => n + c.flags.length, 0);\n\n return (\n <TabWrapper>\n <section className=\"flex flex-col gap-[13px]\">\n <TabHeader\n title=\"Inclusive language\"\n score={data.ringScore}\n status={data.status}\n statusLabel={data.status === \"good\" ? \"Good\" : \"Needs work\"}\n subtitle={\n <>\n {flagged} phrases flagged across {data.categories.length} categories\n </>\n }\n />\n\n {data.categories.length > 0 && (\n <SectionCard title=\"Marked by category\" widget={<Pill variant=\"neutral\">{flagged}</Pill>}>\n {data.categories.map((cat) => (\n <div className={cn(\"relative px-[15px] py-[12px]\", ROW_SEPARATOR)} key={cat.name}>\n <div className=\"flex items-center gap-[8px] mb-[9px]\">\n <span className=\"font-bold text-[12.5px]\">{cat.name}</span>\n <Pill variant=\"bad\">{cat.flags.length}</Pill>\n </div>\n {cat.flags.map((f, i) => (\n <div\n className=\"flex items-center gap-[9px] py-[6px] text-[12px]\"\n key={`${f.term}-${i}`}\n >\n <span className=\"text-seo-bad font-medium whitespace-nowrap\">{f.term}</span>\n <span className=\"text-neutral-300\">›</span>\n <span className=\"text-seo-good font-medium flex-1\">{f.suggestion}</span>\n <span className=\"text-neutral-500 font-mono text-[10.5px] whitespace-nowrap\">\n {f.location}\n </span>\n </div>\n ))}\n </div>\n ))}\n </SectionCard>\n )}\n\n {data.cleanCategories.length > 0 && (\n <SectionCard\n title=\"No issues found\"\n widget={<Pill variant=\"neutral\">{data.cleanCategories.length}</Pill>}\n >\n <div className=\"flex gap-[8px] flex-wrap px-[15px] py-[13px]\">\n {data.cleanCategories.map((n) => (\n <span\n className=\"inline-flex items-center gap-[6px] text-[11.5px] text-seo-good bg-seo-good-100 border border-seo-good-200 rounded-[20px] px-[11px] py-[4px] font-medium\"\n key={n}\n >\n ✓ {n}\n </span>\n ))}\n </div>\n </SectionCard>\n )}\n </section>\n </TabWrapper>\n );\n}\n"],"mappings":";AAeQ,SAMI,UANJ,KAMI,YANJ;AAZR,SAAS,IAAI,qBAAqB;AAClC,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAEpB,SAAS,aAAa,EAAE,KAAK,GAA0C;AAC5E,QAAM,UAAU,KAAK,WAAW,OAAO,CAAC,GAAG,MAAM,IAAI,EAAE,MAAM,QAAQ,CAAC;AAEtE,SACE,oBAAC,cACC,+BAAC,aAAQ,WAAU,4BACjB;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,QACb,aAAa,KAAK,WAAW,SAAS,SAAS;AAAA,QAC/C,UACE,iCACG;AAAA;AAAA,UAAQ;AAAA,UAAyB,KAAK,WAAW;AAAA,UAAO;AAAA,WAC3D;AAAA;AAAA,IAEJ;AAAA,IAEC,KAAK,WAAW,SAAS,KACxB,oBAAC,eAAY,OAAM,sBAAqB,QAAQ,oBAAC,QAAK,SAAQ,WAAW,mBAAQ,GAC9E,eAAK,WAAW,IAAI,CAAC,QACpB,qBAAC,SAAI,WAAW,GAAG,gCAAgC,aAAa,GAC9D;AAAA,2BAAC,SAAI,WAAU,wCACb;AAAA,4BAAC,UAAK,WAAU,2BAA2B,cAAI,MAAK;AAAA,QACpD,oBAAC,QAAK,SAAQ,OAAO,cAAI,MAAM,QAAO;AAAA,SACxC;AAAA,MACC,IAAI,MAAM,IAAI,CAAC,GAAG,MACjB;AAAA,QAAC;AAAA;AAAA,UACC,WAAU;AAAA,UAGV;AAAA,gCAAC,UAAK,WAAU,8CAA8C,YAAE,MAAK;AAAA,YACrE,oBAAC,UAAK,WAAU,oBAAmB,oBAAC;AAAA,YACpC,oBAAC,UAAK,WAAU,oCAAoC,YAAE,YAAW;AAAA,YACjE,oBAAC,UAAK,WAAU,8DACb,YAAE,UACL;AAAA;AAAA;AAAA,QAPK,GAAG,EAAE,IAAI,IAAI,CAAC;AAAA,MAQrB,CACD;AAAA,SAjBqE,IAAI,IAkB5E,CACD,GACH;AAAA,IAGD,KAAK,gBAAgB,SAAS,KAC7B;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,QAAQ,oBAAC,QAAK,SAAQ,WAAW,eAAK,gBAAgB,QAAO;AAAA,QAE7D,8BAAC,SAAI,WAAU,gDACZ,eAAK,gBAAgB,IAAI,CAAC,MACzB;AAAA,UAAC;AAAA;AAAA,YACC,WAAU;AAAA,YAEX;AAAA;AAAA,cACI;AAAA;AAAA;AAAA,UAFE;AAAA,QAGP,CACD,GACH;AAAA;AAAA,IACF;AAAA,KAEJ,GACF;AAEJ;","names":[]}
|
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { AnalysisResult } from "../../../engine/types/analysis";
|
|
2
|
+
import type { KeyphraseEntry } from "../keyphraseState";
|
|
2
3
|
export interface KeyphraseTabProps {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
keyphrases: KeyphraseEntry[];
|
|
5
|
+
selectedId: string | null;
|
|
6
|
+
onSelect: (id: string) => void;
|
|
7
|
+
result: AnalysisResult | null;
|
|
6
8
|
analyzing: boolean;
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
onAddRelated: () => void;
|
|
10
|
+
onTextChange: (id: string, text: string) => void;
|
|
11
|
+
onAddSynonym: (id: string, syn: string) => void;
|
|
12
|
+
onRemoveSynonym: (id: string, index: number) => void;
|
|
13
|
+
onRemove: (id: string) => void;
|
|
14
|
+
onSetFocus: (id: string) => void;
|
|
15
|
+
isDuplicate: (id: string, text: string) => boolean;
|
|
9
16
|
}
|
|
10
|
-
export declare function KeyphraseTab({
|
|
17
|
+
export declare function KeyphraseTab({ keyphrases, selectedId: selectedIdProp, onSelect, result, analyzing, onAddRelated, onTextChange, onAddSynonym, onRemoveSynonym, onRemove, onSetFocus, isDuplicate, }: KeyphraseTabProps): import("react/jsx-runtime").JSX.Element;
|
|
11
18
|
//# sourceMappingURL=KeyphraseTab.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"KeyphraseTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/KeyphraseTab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"KeyphraseTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/KeyphraseTab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAkB,MAAM,gCAAgC,CAAC;AACrF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAMxD,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,MAAM,EAAE,cAAc,GAAG,IAAI,CAAC;IAC9B,SAAS,EAAE,OAAO,CAAC;IACnB,YAAY,EAAE,MAAM,IAAI,CAAC;IACzB,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjD,YAAY,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,eAAe,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACrD,QAAQ,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,UAAU,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,WAAW,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CACpD;AAYD,wBAAgB,YAAY,CAAC,EAC3B,UAAU,EACV,UAAU,EAAE,cAAc,EAC1B,QAAQ,EACR,MAAM,EACN,SAAS,EACT,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,QAAQ,EACR,UAAU,EACV,WAAW,GACZ,EAAE,iBAAiB,2CAkGnB"}
|
|
@@ -1,76 +1,117 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useRef } from "react";
|
|
4
|
+
import { KeyphraseDetail } from "./keyphrase/KeyphraseDetail";
|
|
5
|
+
import { KeyphraseRail } from "./keyphrase/KeyphraseRail";
|
|
6
|
+
function resultForEntry(entry, isFocus, result) {
|
|
7
|
+
if (!result)
|
|
8
|
+
return void 0;
|
|
9
|
+
if (isFocus)
|
|
10
|
+
return result.keyphraseText === entry.text ? result.keyphrase : void 0;
|
|
11
|
+
return result.relatedKeyphrases.find((r) => r.text === entry.text)?.result;
|
|
12
|
+
}
|
|
11
13
|
function KeyphraseTab({
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
keyphrases,
|
|
15
|
+
selectedId: selectedIdProp,
|
|
16
|
+
onSelect,
|
|
17
|
+
result,
|
|
15
18
|
analyzing,
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
onAddRelated,
|
|
20
|
+
onTextChange,
|
|
21
|
+
onAddSynonym,
|
|
22
|
+
onRemoveSynonym,
|
|
23
|
+
onRemove,
|
|
24
|
+
onSetFocus,
|
|
25
|
+
isDuplicate
|
|
18
26
|
}) {
|
|
19
|
-
const [
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
27
|
+
const selectedId = selectedIdProp ?? keyphrases[0]?.id ?? null;
|
|
28
|
+
const selected = keyphrases.find((k) => k.id === selectedId) ?? keyphrases[0] ?? null;
|
|
29
|
+
const selectedIsFocus = selected ? keyphrases.indexOf(selected) === 0 : true;
|
|
30
|
+
const lastMetrics = useRef(/* @__PURE__ */ new Map());
|
|
31
|
+
const liveIds = new Set(keyphrases.map((k) => k.id));
|
|
32
|
+
for (const id of [...lastMetrics.current.keys()]) {
|
|
33
|
+
if (!liveIds.has(id))
|
|
34
|
+
lastMetrics.current.delete(id);
|
|
35
|
+
}
|
|
36
|
+
keyphrases.forEach((entry, index) => {
|
|
37
|
+
if (entry.text.trim() === "") {
|
|
38
|
+
lastMetrics.current.delete(entry.id);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const fresh = resultForEntry(entry, index === 0, result);
|
|
42
|
+
if (fresh)
|
|
43
|
+
lastMetrics.current.set(entry.id, fresh);
|
|
44
|
+
});
|
|
45
|
+
const stateFor = (entry) => {
|
|
46
|
+
if (!entry.text.trim())
|
|
47
|
+
return { kind: "idle" };
|
|
48
|
+
const isFocus = keyphrases.indexOf(entry) === 0;
|
|
49
|
+
if (!isFocus && isDuplicate(entry.id, entry.text))
|
|
50
|
+
return { kind: "idle" };
|
|
51
|
+
const category = resultForEntry(entry, isFocus, result);
|
|
52
|
+
if (category)
|
|
53
|
+
return { kind: "score", score: category.ringScore, status: category.status };
|
|
54
|
+
return { kind: "analyzing" };
|
|
55
|
+
};
|
|
56
|
+
const detailAnalysis = () => {
|
|
57
|
+
if (!selected)
|
|
58
|
+
return { kind: "hint", message: "" };
|
|
59
|
+
if (isDuplicate(selected.id, selected.text)) {
|
|
60
|
+
return {
|
|
61
|
+
kind: "hint",
|
|
62
|
+
message: "Resolve the duplicate to analyse this keyphrase."
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
if (!selected.text.trim()) {
|
|
66
|
+
return {
|
|
67
|
+
kind: "hint",
|
|
68
|
+
message: "Start typing a keyphrase to see its analysis."
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
const category = resultForEntry(selected, selectedIsFocus, result);
|
|
72
|
+
if (category) {
|
|
73
|
+
return {
|
|
74
|
+
kind: "metrics",
|
|
75
|
+
result: category,
|
|
76
|
+
dim: analyzing
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const previous = lastMetrics.current.get(selected.id);
|
|
80
|
+
if (previous) {
|
|
81
|
+
return {
|
|
82
|
+
kind: "metrics",
|
|
83
|
+
result: previous,
|
|
84
|
+
dim: true
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
return { kind: "analyzing" };
|
|
88
|
+
};
|
|
89
|
+
const analysis = detailAnalysis();
|
|
90
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex min-h-[420px]", children: [
|
|
91
|
+
/* @__PURE__ */ jsx(
|
|
92
|
+
KeyphraseRail,
|
|
93
|
+
{
|
|
94
|
+
entries: keyphrases,
|
|
95
|
+
onAdd: onAddRelated,
|
|
96
|
+
onSelect,
|
|
97
|
+
selectedId,
|
|
98
|
+
stateFor
|
|
99
|
+
}
|
|
100
|
+
),
|
|
101
|
+
selected && /* @__PURE__ */ jsx(
|
|
102
|
+
KeyphraseDetail,
|
|
103
|
+
{
|
|
104
|
+
analysis,
|
|
105
|
+
duplicate: isDuplicate(selected.id, selected.text),
|
|
106
|
+
entry: selected,
|
|
107
|
+
isFocus: selectedIsFocus,
|
|
108
|
+
onAddSynonym: (syn) => onAddSynonym(selected.id, syn),
|
|
109
|
+
onRemove: () => onRemove(selected.id),
|
|
110
|
+
onRemoveSynonym: (index) => onRemoveSynonym(selected.id, index),
|
|
111
|
+
onSetFocus: () => onSetFocus(selected.id),
|
|
112
|
+
onTextChange: (text) => onTextChange(selected.id, text)
|
|
113
|
+
}
|
|
114
|
+
)
|
|
74
115
|
] });
|
|
75
116
|
}
|
|
76
117
|
export {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/SeoDrawer/tabs/KeyphraseTab.tsx"],"sourcesContent":["\"use client\";\n\nimport {
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/SeoDrawer/tabs/KeyphraseTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useRef } from \"react\";\nimport type { AnalysisResult, CategoryResult } from \"../../../engine/types/analysis\";\nimport type { KeyphraseEntry } from \"../keyphraseState\";\nimport type { CardState } from \"./keyphrase/KeyphraseCard\";\nimport { KeyphraseDetail } from \"./keyphrase/KeyphraseDetail\";\nimport type { DetailAnalysis } from \"./keyphrase/KeyphraseDetail\";\nimport { KeyphraseRail } from \"./keyphrase/KeyphraseRail\";\n\nexport interface KeyphraseTabProps {\n keyphrases: KeyphraseEntry[];\n selectedId: string | null;\n onSelect: (id: string) => void;\n result: AnalysisResult | null;\n analyzing: boolean;\n onAddRelated: () => void;\n onTextChange: (id: string, text: string) => void;\n onAddSynonym: (id: string, syn: string) => void;\n onRemoveSynonym: (id: string, index: number) => void;\n onRemove: (id: string) => void;\n onSetFocus: (id: string) => void;\n isDuplicate: (id: string, text: string) => boolean;\n}\n\nfunction resultForEntry(\n entry: KeyphraseEntry,\n isFocus: boolean,\n result: AnalysisResult | null\n): CategoryResult | undefined {\n if (!result) return undefined;\n if (isFocus) return result.keyphraseText === entry.text ? result.keyphrase : undefined;\n return result.relatedKeyphrases.find((r) => r.text === entry.text)?.result;\n}\n\nexport function KeyphraseTab({\n keyphrases,\n selectedId: selectedIdProp,\n onSelect,\n result,\n analyzing,\n onAddRelated,\n onTextChange,\n onAddSynonym,\n onRemoveSynonym,\n onRemove,\n onSetFocus,\n isDuplicate,\n}: KeyphraseTabProps) {\n const selectedId = selectedIdProp ?? keyphrases[0]?.id ?? null;\n const selected = keyphrases.find((k) => k.id === selectedId) ?? keyphrases[0] ?? null;\n const selectedIsFocus = selected ? keyphrases.indexOf(selected) === 0 : true;\n\n const lastMetrics = useRef<Map<string, CategoryResult>>(new Map());\n const liveIds = new Set(keyphrases.map((k) => k.id));\n\n for (const id of [...lastMetrics.current.keys()]) {\n if (!liveIds.has(id)) lastMetrics.current.delete(id);\n }\n\n keyphrases.forEach((entry, index) => {\n if (entry.text.trim() === \"\") {\n lastMetrics.current.delete(entry.id);\n return;\n }\n const fresh = resultForEntry(entry, index === 0, result);\n if (fresh) lastMetrics.current.set(entry.id, fresh);\n });\n\n const stateFor = (entry: KeyphraseEntry): CardState => {\n if (!entry.text.trim()) return { kind: \"idle\" };\n\n const isFocus = keyphrases.indexOf(entry) === 0;\n if (!isFocus && isDuplicate(entry.id, entry.text)) return { kind: \"idle\" };\n\n const category = resultForEntry(entry, isFocus, result);\n if (category) return { kind: \"score\", score: category.ringScore, status: category.status };\n\n return { kind: \"analyzing\" };\n };\n\n const detailAnalysis = (): DetailAnalysis => {\n if (!selected) return { kind: \"hint\", message: \"\" };\n\n if (isDuplicate(selected.id, selected.text)) {\n return {\n kind: \"hint\",\n message: \"Resolve the duplicate to analyse this keyphrase.\",\n };\n }\n\n if (!selected.text.trim()) {\n return {\n kind: \"hint\",\n message: \"Start typing a keyphrase to see its analysis.\",\n };\n }\n\n const category = resultForEntry(selected, selectedIsFocus, result);\n if (category) {\n return {\n kind: \"metrics\",\n result: category,\n dim: analyzing,\n };\n }\n\n const previous = lastMetrics.current.get(selected.id);\n if (previous) {\n return {\n kind: \"metrics\",\n result: previous,\n dim: true,\n };\n }\n\n return { kind: \"analyzing\" };\n };\n\n const analysis = detailAnalysis();\n\n return (\n <div className=\"flex min-h-[420px]\">\n <KeyphraseRail\n entries={keyphrases}\n onAdd={onAddRelated}\n onSelect={onSelect}\n selectedId={selectedId}\n stateFor={stateFor}\n />\n\n {selected && (\n <KeyphraseDetail\n analysis={analysis}\n duplicate={isDuplicate(selected.id, selected.text)}\n entry={selected}\n isFocus={selectedIsFocus}\n onAddSynonym={(syn) => onAddSynonym(selected.id, syn)}\n onRemove={() => onRemove(selected.id)}\n onRemoveSynonym={(index) => onRemoveSynonym(selected.id, index)}\n onSetFocus={() => onSetFocus(selected.id)}\n onTextChange={(text) => onTextChange(selected.id, text)}\n />\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OnPageTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/OnPageTab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"OnPageTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/OnPageTab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AASrE,wBAAgB,SAAS,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,2CA4B3D"}
|
|
@@ -6,11 +6,12 @@ import { SectionCard } from "../../../ui/SectionCard";
|
|
|
6
6
|
import { Pill } from "../../../ui/Pill";
|
|
7
7
|
import { FilterPills } from "../../../ui/FilterPills";
|
|
8
8
|
import { CheckRow } from "../../../ui/CheckRow";
|
|
9
|
+
import { TabWrapper } from "../components/TabWrapper";
|
|
9
10
|
function OnPageTab({ data }) {
|
|
10
11
|
const [filter, setFilter] = useState("all");
|
|
11
12
|
const visible = data.checks.filter((c) => filter === "all" || c.status === filter);
|
|
12
13
|
const passing = data.checks.filter((c) => c.status === "good").length;
|
|
13
|
-
return /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-[13px]", children: [
|
|
14
|
+
return /* @__PURE__ */ jsx(TabWrapper, { children: /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-[13px]", children: [
|
|
14
15
|
/* @__PURE__ */ jsx(
|
|
15
16
|
TabHeader,
|
|
16
17
|
{
|
|
@@ -29,7 +30,7 @@ function OnPageTab({ data }) {
|
|
|
29
30
|
/* @__PURE__ */ jsx(FilterPills, { checks: data.checks, value: filter, onChange: setFilter }),
|
|
30
31
|
visible.map((c) => /* @__PURE__ */ jsx(CheckRow, { check: c }, c.id))
|
|
31
32
|
] })
|
|
32
|
-
] });
|
|
33
|
+
] }) });
|
|
33
34
|
}
|
|
34
35
|
export {
|
|
35
36
|
OnPageTab
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/SeoDrawer/tabs/OnPageTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { CategoryResult } from \"../../../engine/types/analysis\";\nimport { TabHeader } from \"../../../ui/TabHeader\";\nimport { SectionCard } from \"../../../ui/SectionCard\";\nimport { Pill } from \"../../../ui/Pill\";\nimport { FilterPills } from \"../../../ui/FilterPills\";\nimport type { Filter } from \"../../../ui/FilterPills\";\nimport { CheckRow } from \"../../../ui/CheckRow\";\n\nexport function OnPageTab({ data }: { data: CategoryResult }) {\n const [filter, setFilter] = useState<Filter>(\"all\");\n const visible = data.checks.filter((c) => filter === \"all\" || c.status === filter);\n const passing = data.checks.filter((c) => c.status === \"good\").length;\n\n return (\n <section className=\"flex flex-col gap-[13px]\">\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/SeoDrawer/tabs/OnPageTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { CategoryResult } from \"../../../engine/types/analysis\";\nimport { TabHeader } from \"../../../ui/TabHeader\";\nimport { SectionCard } from \"../../../ui/SectionCard\";\nimport { Pill } from \"../../../ui/Pill\";\nimport { FilterPills } from \"../../../ui/FilterPills\";\nimport type { Filter } from \"../../../ui/FilterPills\";\nimport { CheckRow } from \"../../../ui/CheckRow\";\nimport { TabWrapper } from \"../components/TabWrapper\";\n\nexport function OnPageTab({ data }: { data: CategoryResult }) {\n const [filter, setFilter] = useState<Filter>(\"all\");\n const visible = data.checks.filter((c) => filter === \"all\" || c.status === filter);\n const passing = data.checks.filter((c) => c.status === \"good\").length;\n\n return (\n <TabWrapper>\n <section className=\"flex flex-col gap-[13px]\">\n <TabHeader\n title=\"On-page structure\"\n score={data.ringScore}\n status={data.status}\n subtitle={\n <>\n {passing} / {data.checks.length} checks passing\n </>\n }\n />\n\n <SectionCard title=\"Checks\" widget={<Pill variant=\"neutral\">{data.checks.length}</Pill>}>\n <FilterPills checks={data.checks} value={filter} onChange={setFilter} />\n {visible.map((c) => (\n <CheckRow key={c.id} check={c} />\n ))}\n </SectionCard>\n </section>\n </TabWrapper>\n );\n}\n"],"mappings":";AAoBQ,SAKI,UALJ,KAKI,YALJ;AAlBR,SAAS,gBAAgB;AAEzB,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AACrB,SAAS,mBAAmB;AAE5B,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAEpB,SAAS,UAAU,EAAE,KAAK,GAA6B;AAC5D,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAiB,KAAK;AAClD,QAAM,UAAU,KAAK,OAAO,OAAO,CAAC,MAAM,WAAW,SAAS,EAAE,WAAW,MAAM;AACjF,QAAM,UAAU,KAAK,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE;AAE/D,SACE,oBAAC,cACC,+BAAC,aAAQ,WAAU,4BACjB;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,QACb,UACE,iCACG;AAAA;AAAA,UAAQ;AAAA,UAAI,KAAK,OAAO;AAAA,UAAO;AAAA,WAClC;AAAA;AAAA,IAEJ;AAAA,IAEA,qBAAC,eAAY,OAAM,UAAS,QAAQ,oBAAC,QAAK,SAAQ,WAAW,eAAK,OAAO,QAAO,GAC9E;AAAA,0BAAC,eAAY,QAAQ,KAAK,QAAQ,OAAO,QAAQ,UAAU,WAAW;AAAA,MACrE,QAAQ,IAAI,CAAC,MACZ,oBAAC,YAAoB,OAAO,KAAb,EAAE,EAAc,CAChC;AAAA,OACH;AAAA,KACF,GACF;AAEJ;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReadabilityTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/ReadabilityTab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"ReadabilityTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/ReadabilityTab.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gCAAgC,CAAC;AASrE,wBAAgB,cAAc,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,2CA4BhE"}
|
|
@@ -6,11 +6,12 @@ import { SectionCard } from "../../../ui/SectionCard";
|
|
|
6
6
|
import { Pill } from "../../../ui/Pill";
|
|
7
7
|
import { FilterPills } from "../../../ui/FilterPills";
|
|
8
8
|
import { CheckRow } from "../../../ui/CheckRow";
|
|
9
|
+
import { TabWrapper } from "../components/TabWrapper";
|
|
9
10
|
function ReadabilityTab({ data }) {
|
|
10
11
|
const [filter, setFilter] = useState("all");
|
|
11
12
|
const visible = data.checks.filter((c) => filter === "all" || c.status === filter);
|
|
12
13
|
const passing = data.checks.filter((c) => c.status === "good").length;
|
|
13
|
-
return /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-[13px]", children: [
|
|
14
|
+
return /* @__PURE__ */ jsx(TabWrapper, { children: /* @__PURE__ */ jsxs("section", { className: "flex flex-col gap-[13px]", children: [
|
|
14
15
|
/* @__PURE__ */ jsx(
|
|
15
16
|
TabHeader,
|
|
16
17
|
{
|
|
@@ -29,7 +30,7 @@ function ReadabilityTab({ data }) {
|
|
|
29
30
|
/* @__PURE__ */ jsx(FilterPills, { checks: data.checks, value: filter, onChange: setFilter }),
|
|
30
31
|
visible.map((c) => /* @__PURE__ */ jsx(CheckRow, { check: c }, c.id))
|
|
31
32
|
] })
|
|
32
|
-
] });
|
|
33
|
+
] }) });
|
|
33
34
|
}
|
|
34
35
|
export {
|
|
35
36
|
ReadabilityTab
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/SeoDrawer/tabs/ReadabilityTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { CategoryResult } from \"../../../engine/types/analysis\";\nimport { TabHeader } from \"../../../ui/TabHeader\";\nimport { SectionCard } from \"../../../ui/SectionCard\";\nimport { Pill } from \"../../../ui/Pill\";\nimport { FilterPills } from \"../../../ui/FilterPills\";\nimport type { Filter } from \"../../../ui/FilterPills\";\nimport { CheckRow } from \"../../../ui/CheckRow\";\n\nexport function ReadabilityTab({ data }: { data: CategoryResult }) {\n const [filter, setFilter] = useState<Filter>(\"all\");\n const visible = data.checks.filter((c) => filter === \"all\" || c.status === filter);\n const passing = data.checks.filter((c) => c.status === \"good\").length;\n\n return (\n <section className=\"flex flex-col gap-[13px]\">\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/SeoDrawer/tabs/ReadabilityTab.tsx"],"sourcesContent":["\"use client\";\n\nimport { useState } from \"react\";\nimport type { CategoryResult } from \"../../../engine/types/analysis\";\nimport { TabHeader } from \"../../../ui/TabHeader\";\nimport { SectionCard } from \"../../../ui/SectionCard\";\nimport { Pill } from \"../../../ui/Pill\";\nimport { FilterPills } from \"../../../ui/FilterPills\";\nimport type { Filter } from \"../../../ui/FilterPills\";\nimport { CheckRow } from \"../../../ui/CheckRow\";\nimport { TabWrapper } from \"../components/TabWrapper\";\n\nexport function ReadabilityTab({ data }: { data: CategoryResult }) {\n const [filter, setFilter] = useState<Filter>(\"all\");\n const visible = data.checks.filter((c) => filter === \"all\" || c.status === filter);\n const passing = data.checks.filter((c) => c.status === \"good\").length;\n\n return (\n <TabWrapper>\n <section className=\"flex flex-col gap-[13px]\">\n <TabHeader\n title=\"Readability\"\n score={data.ringScore}\n status={data.status}\n subtitle={\n <>\n {passing} / {data.checks.length} checks passing\n </>\n }\n />\n\n <SectionCard title=\"Checks\" widget={<Pill variant=\"neutral\">{data.checks.length}</Pill>}>\n <FilterPills checks={data.checks} value={filter} onChange={setFilter} />\n {visible.map((c) => (\n <CheckRow key={c.id} check={c} />\n ))}\n </SectionCard>\n </section>\n </TabWrapper>\n );\n}\n"],"mappings":";AAoBQ,SAKI,UALJ,KAKI,YALJ;AAlBR,SAAS,gBAAgB;AAEzB,SAAS,iBAAiB;AAC1B,SAAS,mBAAmB;AAC5B,SAAS,YAAY;AACrB,SAAS,mBAAmB;AAE5B,SAAS,gBAAgB;AACzB,SAAS,kBAAkB;AAEpB,SAAS,eAAe,EAAE,KAAK,GAA6B;AACjE,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAiB,KAAK;AAClD,QAAM,UAAU,KAAK,OAAO,OAAO,CAAC,MAAM,WAAW,SAAS,EAAE,WAAW,MAAM;AACjF,QAAM,UAAU,KAAK,OAAO,OAAO,CAAC,MAAM,EAAE,WAAW,MAAM,EAAE;AAE/D,SACE,oBAAC,cACC,+BAAC,aAAQ,WAAU,4BACjB;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAM;AAAA,QACN,OAAO,KAAK;AAAA,QACZ,QAAQ,KAAK;AAAA,QACb,UACE,iCACG;AAAA;AAAA,UAAQ;AAAA,UAAI,KAAK,OAAO;AAAA,UAAO;AAAA,WAClC;AAAA;AAAA,IAEJ;AAAA,IAEA,qBAAC,eAAY,OAAM,UAAS,QAAQ,oBAAC,QAAK,SAAQ,WAAW,eAAK,OAAO,QAAO,GAC9E;AAAA,0BAAC,eAAY,QAAQ,KAAK,QAAQ,OAAO,QAAQ,UAAU,WAAW;AAAA,MACrE,QAAQ,IAAI,CAAC,MACZ,oBAAC,YAAoB,OAAO,KAAb,EAAE,EAAc,CAChC;AAAA,OACH;AAAA,KACF,GACF;AAEJ;","names":[]}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { SerpResult } from "../../../engine/types/analysis";
|
|
2
|
-
export declare function SerpTab({ data, keyphrase, faviconUrl, }: {
|
|
2
|
+
export declare function SerpTab({ data, keyphrase, synonyms, faviconUrl, }: {
|
|
3
3
|
data: SerpResult;
|
|
4
4
|
keyphrase: string;
|
|
5
|
+
synonyms?: string[];
|
|
5
6
|
faviconUrl: string;
|
|
6
7
|
}): import("react/jsx-runtime").JSX.Element;
|
|
7
8
|
//# sourceMappingURL=SerpTab.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SerpTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/SerpTab.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;
|
|
1
|
+
{"version":3,"file":"SerpTab.d.ts","sourceRoot":"","sources":["../../../../src/components/SeoDrawer/tabs/SerpTab.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AAOjE,wBAAgB,OAAO,CAAC,EACtB,IAAI,EACJ,SAAS,EACT,QAAa,EACb,UAAU,GACX,EAAE;IACD,IAAI,EAAE,UAAU,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;CACpB,2CAyCA"}
|