diff --git a/css/style.css b/css/style.css index c7c92e8..174c914 100644 --- a/css/style.css +++ b/css/style.css @@ -3946,6 +3946,143 @@ html { text-underline-offset: 2px; } +.lexeme-analysis-card { + margin: 0 0 14px 0; + padding: 14px; + border: 1px solid var(--input-border); + border-radius: 8px; + background: + linear-gradient(135deg, rgba(25, 118, 210, 0.08), rgba(102, 187, 106, 0.06)), + var(--main-bg-color); + box-shadow: 0 4px 14px rgba(0,0,0,0.08); +} + +.lexeme-analysis-header { + display: flex; + gap: 16px; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 12px; +} + +.lexeme-analysis-header h4 { + margin: 2px 0 6px 0; + font-size: 1rem; +} + +.lexeme-analysis-header p { + margin: 0; + color: var(--text-muted); + line-height: 1.5; +} + +.lexeme-analysis-kicker { + font-size: 0.72rem; + font-weight: 700; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--accent-color); +} + +.lexeme-neutralize-btn { + align-self: center; +} + +.lexeme-analysis-list { + display: grid; + gap: 10px; +} + +.lexeme-analysis-item { + border: 1px solid var(--input-border); + border-radius: 8px; + background: rgba(255,255,255,0.03); + padding: 12px; +} + +.lexeme-analysis-item-top { + display: flex; + gap: 12px; + align-items: flex-start; + justify-content: space-between; + margin-bottom: 8px; +} + +.lexeme-analysis-term-group { + display: flex; + flex-wrap: wrap; + gap: 6px; + align-items: center; +} + +.lexeme-term { + font-size: 0.95rem; + font-weight: 700; +} + +.lexeme-chip { + display: inline-flex; + align-items: center; + padding: 2px 8px; + border-radius: 999px; + border: 1px solid var(--input-border); + background: var(--secondary-bg); + color: var(--text-muted); + font-size: 0.74rem; + line-height: 1.4; +} + +.lexeme-analysis-domain { + color: var(--text-muted); + font-size: 0.82rem; + text-align: right; +} + +.lexeme-analysis-rationale { + margin: 0 0 10px 0; + line-height: 1.45; + color: var(--text-color); +} + +.lexeme-analysis-rewrites { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.lexeme-rewrite-btn { + padding: 7px 10px; + border-radius: 999px; + border: 1px solid rgba(25, 118, 210, 0.35); + background: rgba(25, 118, 210, 0.08); + color: var(--text-color); + font: inherit; + font-size: 0.82rem; + cursor: pointer; + transition: transform 0.15s ease, box-shadow 0.15s ease, background 0.15s ease; +} + +.lexeme-rewrite-btn:hover { + transform: translateY(-1px); + background: rgba(25, 118, 210, 0.14); + box-shadow: 0 4px 10px rgba(25, 118, 210, 0.14); +} + +@media (max-width: 720px) { + .lexeme-analysis-header, + .lexeme-analysis-item-top { + flex-direction: column; + } + + .lexeme-analysis-domain { + text-align: left; + } + + .lexeme-neutralize-btn { + width: 100%; + } +} + .bijection-section .bij-options.options-grid { margin-top: 0; margin-bottom: 8px; @@ -5139,4 +5276,3 @@ html { margin-left: var(--spacing-md); } } - diff --git a/index.template.html b/index.template.html index 9f820ed..004bbaa 100644 --- a/index.template.html +++ b/index.template.html @@ -410,6 +410,7 @@ + @@ -426,6 +427,7 @@ + diff --git a/js/core/lexemeAnalysis.js b/js/core/lexemeAnalysis.js new file mode 100644 index 0000000..337e0f1 --- /dev/null +++ b/js/core/lexemeAnalysis.js @@ -0,0 +1,180 @@ +/** + * Shared Latin-root lexeme analysis and neutral rewrite generation. + */ +(function(root) { + function uniq(values) { + return Array.from(new Set(values.filter(Boolean))); + } + + function titleCase(text) { + return String(text || '').replace(/\b[a-z]/g, function(match) { + return match.toUpperCase(); + }); + } + + function preserveCase(source, replacement) { + if (!source) { + return replacement; + } + if (source === source.toUpperCase()) { + return replacement.toUpperCase(); + } + if (source[0] === source[0].toUpperCase() && source.slice(1) === source.slice(1).toLowerCase()) { + return titleCase(replacement); + } + return replacement; + } + + function normalizeRoot(rawRoot, aliases) { + const rootText = String(rawRoot || '').toLowerCase(); + if (!rootText) { + return ''; + } + + if (aliases[rootText]) { + return aliases[rootText]; + } + + const variants = [ + rootText.endsWith('i') ? rootText.slice(0, -1) : '', + rootText.endsWith('ic') ? rootText.slice(0, -2) : '', + rootText.endsWith('o') ? rootText.slice(0, -1) : '', + rootText.endsWith('al') ? rootText.slice(0, -2) : '' + ].filter(Boolean); + + for (const variant of variants) { + if (aliases[variant]) { + return aliases[variant]; + } + } + + return ''; + } + + function materializeTemplates(templates, domain) { + return uniq((templates || []).map(function(template) { + return String(template || '').replace(/\{domain\}/g, domain || 'risk'); + })); + } + + function resolveSuggestions(policy, domain) { + if (domain) { + return materializeTemplates(policy.rewriteTemplates, domain); + } + return uniq(policy.fallbackTemplates || []); + } + + function analyze(text, options) { + const input = String(text || ''); + const policies = (options && options.policies) || root.LATIN_AFFIX_POLICIES || []; + const aliases = (options && options.aliases) || root.LATIN_DOMAIN_ALIASES || {}; + + if (!input.trim()) { + return { + sourceText: input, + totalFindings: 0, + findings: [], + families: [], + summary: 'No Latin-root wording findings.' + }; + } + + const findings = []; + const seen = new Set(); + + policies.forEach(function(policy) { + (policy.patterns || []).forEach(function(pattern) { + let match; + while ((match = pattern.exec(input)) !== null) { + const term = match[0]; + const key = policy.id + '::' + term.toLowerCase(); + if (seen.has(key)) { + continue; + } + seen.add(key); + + const rootCandidate = match[1] || ''; + const semanticDomain = normalizeRoot(rootCandidate, aliases); + const rewrites = resolveSuggestions(policy, semanticDomain); + + findings.push({ + id: key, + term, + normalizedTerm: term.toLowerCase(), + family: policy.family, + policyId: policy.id, + affixes: policy.affixes || [], + partOfSpeech: policy.partOfSpeech, + extractedRoot: rootCandidate || '', + semanticDomain: semanticDomain || '', + severity: policy.severity, + confidence: policy.confidence, + semanticShift: policy.semanticShift, + rationale: policy.explanation, + rewrites, + primaryRewrite: rewrites[0] || '', + matchIndex: match.index + }); + } + pattern.lastIndex = 0; + }); + }); + + findings.sort(function(a, b) { + return a.matchIndex - b.matchIndex; + }); + + return { + sourceText: input, + totalFindings: findings.length, + findings, + families: uniq(findings.map(function(item) { return item.family; })), + summary: findings.length + ? 'Detected ' + findings.length + ' Latin-root wording pattern' + (findings.length === 1 ? '' : 's') + '.' + : 'No Latin-root wording findings.' + }; + } + + function neutralizeText(text, analysis) { + const input = String(text || ''); + if (!analysis || !Array.isArray(analysis.findings) || !analysis.findings.length) { + return input; + } + + let output = input; + analysis.findings.forEach(function(finding) { + if (!finding.primaryRewrite) { + return; + } + const matcher = new RegExp('\\b' + escapeRegExp(finding.term) + '\\b', 'gi'); + output = output.replace(matcher, function(match) { + return preserveCase(match, finding.primaryRewrite); + }); + }); + return output; + } + + function escapeRegExp(value) { + return String(value || '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + } + + const api = { + analyze, + neutralizeText + }; + + if (typeof module !== 'undefined' && module.exports) { + const data = require('../data/latinAffixPolicies.js'); + module.exports = { + analyze: function(text) { + return analyze(text, { + policies: data.POLICIES, + aliases: data.DOMAIN_ALIASES + }); + }, + neutralizeText + }; + } else { + root.LexemeAnalysis = api; + } +})(typeof window !== 'undefined' ? window : globalThis); diff --git a/js/data/latinAffixPolicies.js b/js/data/latinAffixPolicies.js new file mode 100644 index 0000000..7e2a955 --- /dev/null +++ b/js/data/latinAffixPolicies.js @@ -0,0 +1,152 @@ +/** + * Declarative Latin-root prompt wording policies. + * Families define how loaded affixes should be interpreted and rewritten. + */ +(function(root) { + const DOMAIN_ALIASES = { + bacter: 'bacterial', + bacteri: 'bacterial', + bio: 'biological', + bi: 'biological', + fung: 'fungal', + fungi: 'fungal', + germ: 'microbial', + herb: 'weed', + homic: 'violence', + human: 'human', + insect: 'pest', + larv: 'larval', + pestic: 'pest', + pesti: 'pest', + rodent: 'rodent', + suic: 'self-harm', + viru: 'viral', + virus: 'viral' + }; + + const POLICIES = [ + { + id: 'latin-destructive-noun', + family: 'destructive_suffix', + kind: 'suffix', + partOfSpeech: 'noun', + affixes: ['cide'], + patterns: [/\b([a-z]+?)cide(s)?\b/gi], + severity: 'high', + confidence: 0.91, + semanticShift: 'lethal_or_destructive', + explanation: 'The Latin-derived -cide ending tends to foreground killing or eradication.', + rewriteTemplates: [ + '{domain} management', + '{domain} mitigation', + '{domain} control', + '{domain} stewardship' + ], + fallbackTemplates: [ + 'mitigation', + 'management', + 'deterrence', + 'stewardship' + ] + }, + { + id: 'latin-destructive-adjective', + family: 'destructive_suffix', + kind: 'suffix', + partOfSpeech: 'adjective', + affixes: ['cidal'], + patterns: [/\b([a-z]+?)cidal(ly)?\b/gi, /\bcidal(ly)?\b/gi], + severity: 'medium', + confidence: 0.84, + semanticShift: 'destructive_operational_framing', + explanation: 'The Latin-derived -cidal ending can make otherwise neutral activity sound destructive.', + rewriteTemplates: [ + 'focused on {domain} management', + 'used for {domain} mitigation', + 'intended for {domain} control' + ], + fallbackTemplates: [ + 'framed around mitigation', + 'framed around management', + 'operationally neutral' + ] + }, + { + id: 'latin-eradication-verbs', + family: 'eradication_language', + kind: 'surface_pattern', + partOfSpeech: 'verb', + affixes: ['eradicate', 'exterminate', 'annihilate', 'obliterate'], + patterns: [ + /\b(?:eradicate|eradicates|eradicated|eradicating|eradication)\b/gi, + /\b(?:exterminate|exterminates|exterminated|exterminating|extermination)\b/gi, + /\b(?:annihilate|annihilates|annihilated|annihilating|annihilation)\b/gi, + /\b(?:obliterate|obliterates|obliterated|obliterating|obliteration)\b/gi + ], + severity: 'high', + confidence: 0.88, + semanticShift: 'total_removal', + explanation: 'Eradication language implies total destruction rather than bounded handling.', + fallbackTemplates: [ + 'reduce', + 'manage', + 'contain', + 'address' + ] + }, + { + id: 'latin-suppression-language', + family: 'suppression_language', + kind: 'surface_pattern', + partOfSpeech: 'verb', + affixes: ['neutralize', 'incapacitate', 'suppress'], + patterns: [ + /\b(?:neutralize|neutralizes|neutralized|neutralizing|neutralization)\b/gi, + /\b(?:incapacitate|incapacitates|incapacitated|incapacitating|incapacitation)\b/gi, + /\b(?:suppress|suppresses|suppressed|suppressing|suppression)\b/gi + ], + severity: 'medium', + confidence: 0.82, + semanticShift: 'forceful_control', + explanation: 'Suppression language can read as coercive or force-centric rather than operationally neutral.', + fallbackTemplates: [ + 'limit', + 'reduce', + 'constrain', + 'manage' + ] + }, + { + id: 'latin-terminality-language', + family: 'terminality_language', + kind: 'surface_pattern', + partOfSpeech: 'adjective', + affixes: ['lethal', 'fatal', 'terminal', 'mortal'], + patterns: [ + /\b(?:lethal|fatal|terminal|mortal)\b/gi + ], + severity: 'medium', + confidence: 0.8, + semanticShift: 'fatal_outcome_framing', + explanation: 'Terminality language frames outcomes as fatal or irreversible even when a softer operational phrasing would do.', + fallbackTemplates: [ + 'high-risk', + 'severe', + 'critical', + 'unsafe' + ] + } + ]; + + const api = { + DOMAIN_ALIASES, + POLICIES + }; + + if (typeof module !== 'undefined' && module.exports) { + module.exports = api; + } else { + root.LATIN_AFFIX_POLICIES = POLICIES; + root.LATIN_DOMAIN_ALIASES = DOMAIN_ALIASES; + } +})(typeof window !== 'undefined' ? window : globalThis); diff --git a/js/tools/AntiClassifierTool.js b/js/tools/AntiClassifierTool.js index 7dcc4d8..9b53d63 100644 --- a/js/tools/AntiClassifierTool.js +++ b/js/tools/AntiClassifierTool.js @@ -24,6 +24,7 @@ class AntiClassifierTool extends Tool { acInput: '', acOutput: '', acError: '', + acLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }, acLoading: false, acModel: localStorage.getItem('ac-model') || 'anthropic/claude-sonnet-4.6', acModels: models, @@ -49,6 +50,13 @@ class AntiClassifierTool extends Tool { ? window.ANTICLASSIFIER_SYSTEM_PROMPT : ''; }, + acRefreshLexemeAnalysis: function() { + if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') { + this.acLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' }; + return; + } + this.acLexemeAnalysis = window.LexemeAnalysis.analyze(this.acInput); + }, acRun: async function() { const apiKey = this.acGetApiKey(); if (!apiKey) { @@ -123,6 +131,39 @@ class AntiClassifierTool extends Tool { if (this.acOutput) { this.copyToClipboard(this.acOutput); } + }, + acGetLexemeAnalysis: function() { + return this.acLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }; + }, + acNeutralizeInput: function() { + const analysis = this.acGetLexemeAnalysis(); + if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') { + return; + } + this.acInput = window.LexemeAnalysis.neutralizeText(this.acInput, analysis); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling'); + } + }, + acApplyLexemeRewrite: function(term, rewrite) { + if (!term || !rewrite) { + return; + } + const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + this.acInput = this.acInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen'); + } + } + }; + } + + getVueWatchers() { + return { + acInput: function() { + if (typeof this.acRefreshLexemeAnalysis === 'function') { + this.acRefreshLexemeAnalysis(); + } } }; } diff --git a/js/tools/BijectionTool.js b/js/tools/BijectionTool.js index 8bf9f30..bc1606c 100644 --- a/js/tools/BijectionTool.js +++ b/js/tools/BijectionTool.js @@ -20,6 +20,7 @@ class BijectionTool extends Tool { bijectionIncludeExamples: true, bijectionAutoCopy: false, bijectionInput: '', + bijectionLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }, bijectionMapping: {}, bijectionOutputs: [] }; @@ -27,6 +28,36 @@ class BijectionTool extends Tool { getVueMethods() { return { + bijectionRefreshLexemeAnalysis() { + if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') { + this.bijectionLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' }; + return; + } + this.bijectionLexemeAnalysis = window.LexemeAnalysis.analyze(this.bijectionInput); + }, + bijectionGetLexemeAnalysis() { + return this.bijectionLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }; + }, + bijectionNeutralizeInput() { + const analysis = this.bijectionGetLexemeAnalysis(); + if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') { + return; + } + this.bijectionInput = window.LexemeAnalysis.neutralizeText(this.bijectionInput, analysis); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling'); + } + }, + bijectionApplyLexemeRewrite(term, rewrite) { + if (!term || !rewrite) { + return; + } + const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + this.bijectionInput = this.bijectionInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen'); + } + }, generateBijectionMapping() { const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.,!?'; const mapping = {}; @@ -229,6 +260,16 @@ Now, please translate and respond to this message in alphapr: ${encoded}`; } }; } + + getVueWatchers() { + return { + bijectionInput: function() { + if (typeof this.bijectionRefreshLexemeAnalysis === 'function') { + this.bijectionRefreshLexemeAnalysis(); + } + } + }; + } } if (typeof module !== 'undefined' && module.exports) { diff --git a/js/tools/MutationTool.js b/js/tools/MutationTool.js index 85a30b0..aac960d 100644 --- a/js/tools/MutationTool.js +++ b/js/tools/MutationTool.js @@ -15,6 +15,7 @@ class MutationTool extends Tool { getVueData() { return { fuzzerInput: '', + fuzzerLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }, fuzzerCount: 20, fuzzerSeed: '', fuzzUseRandomMix: true, @@ -30,6 +31,36 @@ class MutationTool extends Tool { getVueMethods() { return { + fuzzerRefreshLexemeAnalysis: function() { + if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') { + this.fuzzerLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' }; + return; + } + this.fuzzerLexemeAnalysis = window.LexemeAnalysis.analyze(this.fuzzerInput); + }, + fuzzerGetLexemeAnalysis: function() { + return this.fuzzerLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }; + }, + fuzzerNeutralizeInput: function() { + const analysis = this.fuzzerGetLexemeAnalysis(); + if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') { + return; + } + this.fuzzerInput = window.LexemeAnalysis.neutralizeText(this.fuzzerInput, analysis); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling'); + } + }, + fuzzerApplyLexemeRewrite: function(term, rewrite) { + if (!term || !rewrite) { + return; + } + const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + this.fuzzerInput = this.fuzzerInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen'); + } + }, seededRandomFactory: function(seedStr) { if (!seedStr) return Math.random; let h = 1779033703 ^ seedStr.length; @@ -106,6 +137,16 @@ class MutationTool extends Tool { } }; } + + getVueWatchers() { + return { + fuzzerInput: function() { + if (typeof this.fuzzerRefreshLexemeAnalysis === 'function') { + this.fuzzerRefreshLexemeAnalysis(); + } + } + }; + } } // Export @@ -116,4 +157,3 @@ if (typeof module !== 'undefined' && module.exports) { } - diff --git a/js/tools/PromptCraftTool.js b/js/tools/PromptCraftTool.js index 0a8e0af..49f21c8 100644 --- a/js/tools/PromptCraftTool.js +++ b/js/tools/PromptCraftTool.js @@ -21,6 +21,7 @@ class PromptCraftTool extends Tool { pcInput: '', pcOutput: '', pcOutputs: [], + pcLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }, pcStrategy: 'rephrase', pcModel: localStorage.getItem('pc-model') || 'nousresearch/hermes-3-llama-3.1-405b', pcTemperature, @@ -78,6 +79,13 @@ class PromptCraftTool extends Tool { } return base; }, + pcRefreshLexemeAnalysis: function() { + if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') { + this.pcLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' }; + return; + } + this.pcLexemeAnalysis = window.LexemeAnalysis.analyze(this.pcInput); + }, pcRunMutation: async function() { const apiKey = this.pcGetApiKey(); if (!apiKey) { @@ -163,6 +171,39 @@ class PromptCraftTool extends Tool { }, pcUseAsInput: function(text) { this.pcInput = text; + }, + pcGetLexemeAnalysis: function() { + return this.pcLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }; + }, + pcNeutralizeInput: function() { + const analysis = this.pcGetLexemeAnalysis(); + if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') { + return; + } + this.pcInput = window.LexemeAnalysis.neutralizeText(this.pcInput, analysis); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling'); + } + }, + pcApplyLexemeRewrite: function(term, rewrite) { + if (!term || !rewrite) { + return; + } + const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + this.pcInput = this.pcInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen'); + } + } + }; + } + + getVueWatchers() { + return { + pcInput: function() { + if (typeof this.pcRefreshLexemeAnalysis === 'function') { + this.pcRefreshLexemeAnalysis(); + } } }; } diff --git a/js/tools/TransformTool.js b/js/tools/TransformTool.js index 39b2b89..990ff1f 100644 --- a/js/tools/TransformTool.js +++ b/js/tools/TransformTool.js @@ -61,6 +61,7 @@ class TransformTool extends Tool { return { transformInput: 'Hello World', + transformLexemeAnalysis: { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }, transformOutput: '', activeTransform: null, transforms: transforms, @@ -250,6 +251,36 @@ class TransformTool extends Tool { } return 'text'; }, + transformRefreshLexemeAnalysis: function() { + if (typeof window === 'undefined' || !window.LexemeAnalysis || typeof window.LexemeAnalysis.analyze !== 'function') { + this.transformLexemeAnalysis = { totalFindings: 0, findings: [], summary: 'Lexeme analysis unavailable.' }; + return; + } + this.transformLexemeAnalysis = window.LexemeAnalysis.analyze(this.transformInput); + }, + transformGetLexemeAnalysis: function() { + return this.transformLexemeAnalysis || { totalFindings: 0, findings: [], summary: 'No Latin-root wording findings.' }; + }, + transformNeutralizeInput: function() { + const analysis = this.transformGetLexemeAnalysis(); + if (!analysis.totalFindings || !window.LexemeAnalysis || typeof window.LexemeAnalysis.neutralizeText !== 'function') { + return; + } + this.transformInput = window.LexemeAnalysis.neutralizeText(this.transformInput, analysis); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied neutral Latin-root rewrites', 'success', 'fas fa-seedling'); + } + }, + transformApplyLexemeRewrite: function(term, rewrite) { + if (!term || !rewrite) { + return; + } + const escapedTerm = String(term).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + this.transformInput = this.transformInput.replace(new RegExp('\\b' + escapedTerm + '\\b', 'i'), rewrite); + if (typeof this.showNotification === 'function') { + this.showNotification('Applied rewrite for ' + term, 'success', 'fas fa-pen'); + } + }, openTransformOptions: function(transform, event) { if (event) { event.preventDefault(); @@ -623,6 +654,9 @@ class TransformTool extends Tool { getVueWatchers() { return { transformInput() { + if (typeof this.transformRefreshLexemeAnalysis === 'function') { + this.transformRefreshLexemeAnalysis(); + } if (this.activeTransform && this.activeTab === 'transforms') { const opts = this.getMergedOptionsForTransform(this.activeTransform.name); this.transformOutput = this.activeTransform.func(this.transformInput, opts); @@ -670,4 +704,3 @@ if (typeof module !== 'undefined' && module.exports) { } - diff --git a/package.json b/package.json index 64a1606..117727e 100644 --- a/package.json +++ b/package.json @@ -13,9 +13,11 @@ "start": "serve dist -l 8080", "preview": "npm run build && serve dist -l 8080", "test": "node tests/test_universal.js", + "test:lexeme": "node tests/test_lexeme_analysis.js", + "test:lexeme-ui": "node tests/test_lexeme_ui_surface.js", "test:universal": "node tests/test_universal.js", "test:steg": "node tests/test_steganography_options.js", - "test:all": "npm run test:universal && npm run test:steg", + "test:all": "npm run test:universal && npm run test:steg && npm run test:lexeme && npm run test:lexeme-ui", "precommit": "npm run test:all" }, "repository": { diff --git a/templates/anticlassifier.html b/templates/anticlassifier.html index c238120..1a3a871 100644 --- a/templates/anticlassifier.html +++ b/templates/anticlassifier.html @@ -13,6 +13,48 @@ +
This input contains affix patterns that skew toward destructive or lethal framing. Use the generated rewrites to neutralize tone before transformation.
+{{ finding.term }}
+ {{ finding.partOfSpeech }}
+ {{ finding.affixes.join(', ') }}
+ confidence {{ Math.round(finding.confidence * 100) }}%
+ {{ finding.rationale }}
+This target content contains loaded Latin-root wording. Neutralizing it here propagates through the generated alphapr prompts.
+{{ finding.term }}
+ {{ finding.partOfSpeech }}
+ {{ finding.family }}
+ confidence {{ Math.round(finding.confidence * 100) }}%
+ {{ finding.rationale }}
+This seed text contains loaded Latin-root wording. Neutralizing it here affects every generated mutation case.
+{{ finding.term }}
+ {{ finding.partOfSpeech }}
+ {{ finding.family }}
+ confidence {{ Math.round(finding.confidence * 100) }}%
+ {{ finding.rationale }}
+This input contains affix patterns that skew toward destructive or lethal framing. Use the generated rewrites to neutralize tone before mutation.
+{{ finding.term }}
+ {{ finding.partOfSpeech }}
+ {{ finding.affixes.join(', ') }}
+ confidence {{ Math.round(finding.confidence * 100) }}%
+ {{ finding.rationale }}
+This shared input feeds transforms and inline translation. Neutralizing flagged wording here affects both paths.
+{{ finding.term }}
+ {{ finding.partOfSpeech }}
+ {{ finding.family }}
+ confidence {{ Math.round(finding.confidence * 100) }}%
+ {{ finding.rationale }}
+