Files
2026-04-05 15:33:42 -04:00

532 lines
37 KiB
HTML

<div v-if="activeTab === 'transforms'" class="tab-content">
<div class="transform-layout">
<div class="input-section">
<textarea
v-if="transformInputControlKind() === 'textarea'"
id="transform-input"
v-model="transformInput"
placeholder="Enter text to transform..."
@input="autoTransform"
></textarea>
<input
v-else
id="transform-input"
type="text"
v-model="transformInput"
placeholder="Enter text to transform..."
@input="autoTransform"
autocomplete="off"
autocorrect="off"
spellcheck="false"
/>
</div>
<div v-if="transformGetLexemeAnalysis().totalFindings" class="lexeme-analysis-card transform-lexeme-card">
<div class="lexeme-analysis-header">
<div>
<div class="lexeme-analysis-kicker">Latin-Root Analysis</div>
<h4>{{ transformGetLexemeAnalysis().summary }}</h4>
<p>This shared input feeds transforms and inline translation. Neutralizing flagged wording here affects both paths.</p>
</div>
<button type="button" class="action-button copy lexeme-neutralize-btn" @click="transformNeutralizeInput">
<i class="fas fa-seedling"></i> Neutralize flagged terms
</button>
</div>
<div class="lexeme-analysis-list">
<div v-for="finding in transformGetLexemeAnalysis().findings" :key="finding.id" class="lexeme-analysis-item">
<div class="lexeme-analysis-item-top">
<div class="lexeme-analysis-term-group">
<code class="lexeme-term">{{ finding.term }}</code>
<span class="lexeme-chip">{{ finding.partOfSpeech }}</span>
<span class="lexeme-chip">{{ finding.family }}</span>
<span class="lexeme-chip">confidence {{ Math.round(finding.confidence * 100) }}%</span>
</div>
<div class="lexeme-analysis-domain">
<span v-if="finding.semanticDomain"><strong>Domain:</strong> {{ finding.semanticDomain }}</span>
<span v-else><strong>Domain:</strong> unresolved root</span>
</div>
</div>
<p class="lexeme-analysis-rationale">{{ finding.rationale }}</p>
<div class="lexeme-analysis-rewrites">
<button v-for="rewrite in finding.rewrites" :key="finding.id + '-' + rewrite" type="button" class="lexeme-rewrite-btn" @click="transformApplyLexemeRewrite(finding.term, rewrite)">
{{ rewrite }}
</button>
</div>
</div>
</div>
</div>
<div class="transform-section">
<div class="transform-category-legend">
<div class="legend-title">Categories:</div>
<div>
<div
v-for="category in legendCategories"
:key="category"
:class="'legend-item transform-category-' + category"
:data-target="'category-' + category"
>
{{ category }}
</div>
</div>
</div>
<div class="transform-categories">
<!-- Favorites Section -->
<div
v-if="showFavorites && getFavoriteDisplayItems().length > 0"
id="category-favorites"
class="transform-category-section favorites-section"
>
<h4 class="category-title">
<i class="fas fa-star"></i> Favorites
</h4>
<div class="transform-buttons">
<template v-for="item in getFavoriteDisplayItems()" :key="item.key">
<div v-if="item.type === 'transform'" class="transform-button-group">
<button
@click="applyTransform(item.transform, $event)"
:class="'transform-button transform-category-' + getDisplayCategory(item.transform.name)"
:class="{ active: activeTransform === item.transform }"
:title="'Click to transform and copy: ' + item.transform.name"
>
{{ item.transform.name }}
<small class="transform-preview" v-if="transformInput">
{{ item.transform.preview(transformInput.slice(0, 10), getMergedOptionsForTransform(item.transform.name)) }}
</small>
<i
v-if="transformHasOptionsUI(item.transform)"
class="fas fa-gear transform-options-gear"
@click.stop="openTransformOptions(item.transform, $event)"
title="Options"
:aria-label="'Options for ' + item.transform.name"
></i>
<i
@click.stop="toggleFavorite(item.transform.name, $event)"
:class="'fas fa-star favorite-icon' + (isFavorite(item.transform.name) ? ' favorited' : '')"
:title="isFavorite(item.transform.name) ? 'Remove from favorites' : 'Add to favorites'"
></i>
</button>
</div>
<div v-else-if="item.type === 'translate'" class="transform-button-group">
<button
type="button"
class="transform-button transform-category-ancient"
@click="translateTo(item.langName)"
:disabled="translateLoading"
:title="'Translate to ' + item.langName"
>
{{ item.langName }}
<small class="transform-preview" v-if="transformInput">
{{ transformInput.slice(0, 10) }}
</small>
<i
@click.stop="toggleTranslateFavorite(item.langName, item.custom, $event)"
class="fas fa-star favorite-icon"
:class="{ favorited: isTranslateFavorite(item.langName, item.custom) }"
:title="isTranslateFavorite(item.langName, item.custom) ? 'Remove from favorites' : 'Add to favorites'"
></i>
</button>
</div>
</template>
</div>
</div>
<!-- Last Used Section -->
<div
v-if="showLastUsed && getLastUsedDisplayItems().length > 0"
id="category-last-used"
class="transform-category-section last-used-section"
>
<h4 class="category-title">
<i class="fas fa-clock"></i> Last Used
</h4>
<div class="transform-buttons">
<template v-for="item in getLastUsedDisplayItems()" :key="item.key">
<div v-if="item.type === 'transform'" class="transform-button-group">
<button
@click="applyTransform(item.transform, $event)"
:class="'transform-button transform-category-' + getDisplayCategory(item.transform.name)"
:class="{ active: activeTransform === item.transform }"
:title="'Click to transform and copy: ' + item.transform.name"
>
{{ item.transform.name }}
<small class="transform-preview" v-if="transformInput">
{{ item.transform.preview(transformInput.slice(0, 10), getMergedOptionsForTransform(item.transform.name)) }}
</small>
<i
v-if="transformHasOptionsUI(item.transform)"
class="fas fa-gear transform-options-gear"
@click.stop="openTransformOptions(item.transform, $event)"
title="Options"
:aria-label="'Options for ' + item.transform.name"
></i>
<i
@click.stop="toggleFavorite(item.transform.name, $event)"
:class="'fas fa-star favorite-icon' + (isFavorite(item.transform.name) ? ' favorited' : '')"
:title="isFavorite(item.transform.name) ? 'Remove from favorites' : 'Add to favorites'"
></i>
</button>
</div>
<div v-else-if="item.type === 'translate'" class="transform-button-group">
<button
type="button"
class="transform-button transform-category-ancient"
@click="translateTo(item.langName)"
:disabled="translateLoading"
:title="'Translate to ' + item.langName"
>
{{ item.langName }}
<small class="transform-preview" v-if="transformInput">
{{ transformInput.slice(0, 10) }}
</small>
<i
@click.stop="toggleTranslateFavorite(item.langName, item.custom, $event)"
class="fas fa-star favorite-icon"
:class="{ favorited: isTranslateFavorite(item.langName, item.custom) }"
:title="isTranslateFavorite(item.langName, item.custom) ? 'Remove from favorites' : 'Add to favorites'"
></i>
</button>
</div>
</template>
</div>
</div>
<template v-for="(category, categoryIndex) in categories" :key="category">
<!-- AI Translation Section — renders above "ancient" -->
<div v-if="category === 'ancient'" id="category-translate" class="transform-category-section translate-inline-section">
<h4 class="category-title transform-category-translate">
<i class="fas fa-language"></i> Translation (AI)
<small class="translate-powered-by">TranslateGemma</small>
</h4>
<div v-if="translateError" class="pc-error">
<i class="fas fa-exclamation-triangle"></i> {{ translateError }}
</div>
<div v-if="translateLoading" class="translate-loading">
<i class="fas fa-spinner fa-spin"></i> Translating to {{ translateActiveLang }}...
</div>
<div class="translate-model-picker">
<select v-model="translateModel" class="translate-model-select">
<option v-for="m in translateModels" :key="m.id" :value="m.id">{{ m.name }} — {{ m.note }}</option>
</select>
</div>
<div class="translate-subsection">
<div class="translate-subsection-label"><i class="fas fa-globe"></i> Major</div>
<div class="translate-lang-grid translate-lang-grid-inline">
<button
v-for="lang in translateMainLangs"
:key="lang.code"
class="translate-lang-btn translate-lang-main"
@click="translateTo(lang.name)"
:disabled="translateLoading"
:title="'Translate to ' + lang.name"
>
<span class="translate-flag">{{ translateGetFlag(lang.flag) }}</span>
<span class="translate-name">{{ lang.name }}</span>
<i
@click.stop="toggleTranslateFavorite(lang.name, false, $event)"
class="fas fa-star favorite-icon translate-lang-favorite"
:class="{ favorited: isTranslateFavorite(lang.name, false) }"
:title="isTranslateFavorite(lang.name, false) ? 'Remove from favorites' : 'Add to favorites'"
></i>
</button>
</div>
</div>
<div class="translate-subsection">
<div class="translate-subsection-label"><i class="fas fa-scroll"></i> Dead &amp; Exotic</div>
<div class="translate-lang-grid translate-lang-grid-inline">
<button
v-for="lang in translateExoticLangs"
:key="lang.code"
class="translate-lang-btn translate-lang-exotic"
@click="translateTo(lang.name)"
:disabled="translateLoading"
:title="'Translate to ' + lang.name"
>
<span class="translate-flag">{{ translateGetFlag(lang.flag) }}</span>
<span class="translate-name">{{ lang.name }}</span>
<i
@click.stop="toggleTranslateFavorite(lang.name, false, $event)"
class="fas fa-star favorite-icon translate-lang-favorite"
:class="{ favorited: isTranslateFavorite(lang.name, false) }"
:title="isTranslateFavorite(lang.name, false) ? 'Remove from favorites' : 'Add to favorites'"
></i>
</button>
</div>
</div>
<div class="translate-subsection">
<button
type="button"
class="translate-subsection-label translate-custom-label-btn"
@click="translateAddingLang = !translateAddingLang"
title="Add a language"
>
<i class="fas fa-plus-circle"></i>
<span>Custom</span>
<span class="translate-custom-toggle-end" aria-hidden="true">
<i :class="translateAddingLang ? 'fas fa-minus' : 'fas fa-plus'"></i>
</span>
</button>
<div v-if="translateAddingLang" class="translate-add-form">
<input
type="text"
v-model="translateNewLangName"
placeholder="e.g., Tagalog, Esperanto, Nahuatl..."
@keyup.enter="translateAddCustomLang"
/>
<button class="translate-add-btn" @click="translateAddCustomLang" :disabled="!translateNewLangName.trim()">
<i class="fas fa-plus"></i> Add
</button>
</div>
<div class="translate-lang-grid translate-lang-grid-inline" v-if="translateCustomLangs.length">
<button
v-for="(lang, idx) in translateCustomLangs"
:key="'custom-'+idx"
class="translate-lang-btn translate-lang-custom"
@click="translateTo(lang.name)"
:disabled="translateLoading"
:title="'Translate to ' + lang.name"
>
<span class="translate-flag translate-custom-flag" aria-hidden="true"><i class="fas fa-globe"></i></span>
<span class="translate-name">{{ lang.name }}</span>
<span class="translate-remove" @click.stop="translateRemoveCustomLang(idx)" title="Remove">&times;</span>
<i
@click.stop="toggleTranslateFavorite(lang.name, true, $event)"
class="fas fa-star favorite-icon translate-lang-favorite"
:class="{ favorited: isTranslateFavorite(lang.name, true) }"
:title="isTranslateFavorite(lang.name, true) ? 'Remove from favorites' : 'Add to favorites'"
></i>
</button>
</div>
<div v-if="!translateCustomLangs.length && !translateAddingLang" class="translate-empty-custom">
<small>Click <strong>Custom</strong> above to add any language.</small>
</div>
</div>
</div>
<!-- Special handling for randomizer category -->
<div
v-if="isSpecialCategory(category)"
:id="'category-' + category"
:class="'transform-category-section randomizer-special'"
>
<h4 :class="'category-title transform-category-' + category">🎲 Randomizer - Code Switching Magic!</h4>
<p class="randomizer-description">
🌟 Apply different transforms to each word in your sentence! Creates a polyglot mix of encodings.
</p>
<div class="chaos-overlay" aria-hidden="true"></div>
<div class="transform-buttons">
<div v-for="transform in getTransformsByCategory(category)" :key="transform.name" class="transform-button-group">
<button
@click="applyTransform(transform, $event)"
:class="'transform-button transform-category-' + category + ' randomizer-button'"
:class="{ active: activeTransform === transform }"
title="Click to apply random transforms to each word!"
>
<i class="fas fa-wand-magic-sparkles"></i>
!RANDOMIZE!
<small class="transform-preview">
🌀 Each word = different transform!
</small>
<i
@click.stop="toggleFavorite(transform.name, $event)"
:class="'fas fa-star favorite-icon' + (isFavorite(transform.name) ? ' favorited' : '')"
:title="isFavorite(transform.name) ? 'Remove from favorites' : 'Add to favorites'"
></i>
</button>
</div>
</div>
<div class="randomizer-info">
<h5>🎮 How it works:</h5>
<ul>
<li>🔀 Each word gets a <strong>random transform</strong></li>
<li>🎯 Mixes <strong>fantasy</strong>, <strong>ancient</strong>, and <strong>modern</strong> encodings</li>
<li>📝 Preserves punctuation and spacing</li>
<li>🔍 Check console for transform mapping details</li>
</ul>
<p><em>Example: "Hello World!" → "SGVsbG8= ᚹᚩᚱᛚᛞ!"</em></p>
</div>
</div>
<!-- Standard category -->
<div
v-else
:id="'category-' + category"
:class="'transform-category-section'"
>
<h4 :class="'category-title transform-category-' + category">
{{ category }}
<span class="category-order-controls">
<button
v-if="categoryIndex > 0"
@click.stop="moveCategoryUp(categoryIndex)"
class="category-move-btn"
title="Move category up"
>
<i class="fas fa-arrow-up"></i>
</button>
<button
v-if="categoryIndex < categories.length - 1"
@click.stop="moveCategoryDown(categoryIndex)"
class="category-move-btn"
title="Move category down"
>
<i class="fas fa-arrow-down"></i>
</button>
</span>
</h4>
<div class="transform-buttons">
<div v-for="transform in getTransformsByCategory(category)" :key="transform.name" class="transform-button-group">
<button
@click="applyTransform(transform, $event)"
:class="'transform-button transform-category-' + category"
:class="{ active: activeTransform === transform }"
:title="'Click to transform and copy: ' + transform.name"
>
{{ transform.name }}
<small class="transform-preview" v-if="transformInput">
{{ transform.preview(transformInput.slice(0, 10), getMergedOptionsForTransform(transform.name)) }}
</small>
<i
v-if="transformHasOptionsUI(transform)"
class="fas fa-gear transform-options-gear"
@click.stop="openTransformOptions(transform, $event)"
title="Options"
:aria-label="'Options for ' + transform.name"
></i>
<i
@click.stop="toggleFavorite(transform.name, $event)"
:class="'fas fa-star favorite-icon' + (isFavorite(transform.name) ? ' favorited' : '')"
:title="isFavorite(transform.name) ? 'Remove from favorites' : 'Add to favorites'"
></i>
</button>
</div>
</div>
</div>
</template>
</div>
</div>
<div class="output-section" v-if="transformOutput">
<div class="output-heading">
<h4>
<i class="fas fa-check-circle"></i>
Transformed Message
<small v-if="activeTransform">({{ activeTransform.name }})</small>
</h4>
</div>
<div class="output-container">
<textarea
readonly
v-model="transformOutput"
aria-label="Transformed text output"
></textarea>
<button class="copy-button" @click="copyToClipboard(transformOutput)" title="Copy to clipboard">
<i class="fas fa-copy"></i>
</button>
</div>
<div class="output-instructions">
<small><i class="fas fa-info-circle"></i> Copy this text and share it. Use the Decoder tab to reverse transformations.</small>
</div>
</div>
</div>
<!-- Native <template> keeps children out of the layout until Vue runs (no modal flash on load). -->
<template v-if="transformOptionsModalOpen && transformOptionsModalTransform">
<div
class="transform-options-backdrop"
@click.self="closeTransformOptions"
>
<div
class="transform-options-panel"
role="dialog"
aria-modal="true"
aria-labelledby="transform-options-title"
@click.stop
>
<div class="transform-options-panel-header">
<h3 id="transform-options-title">{{ transformOptionsModalTransform.name }}</h3>
<button type="button" class="transform-options-close" @click="closeTransformOptions" title="Close" aria-label="Close">
<i class="fas fa-times"></i>
</button>
</div>
<div class="transform-options-panel-body">
<div
v-for="opt in transformOptionsModalTransform.configurableOptions"
:key="opt.id"
class="transform-options-field"
>
<label :for="'opt-' + opt.id">{{ opt.label }}</label>
<template v-if="opt.type === 'boolean'">
<input
:id="'opt-' + opt.id"
class="transform-options-checkbox"
type="checkbox"
:checked="transformOptionsDraft[opt.id]"
@change="setTransformOptionDraft(opt.id, $event.target.checked)"
/>
</template>
<template v-else-if="opt.type === 'select'">
<select
:id="'opt-' + opt.id"
class="transform-options-select"
:value="transformOptionsDraft[opt.id]"
@change="setTransformOptionDraft(opt.id, $event.target.value)"
>
<option
v-for="choice in opt.options"
:key="String(choice.value)"
:value="choice.value"
>
{{ choice.label }}
</option>
</select>
</template>
<template v-else-if="opt.type === 'text'">
<input
:id="'opt-' + opt.id"
type="text"
class="transform-options-text"
:value="transformOptionsDraft[opt.id]"
autocomplete="off"
autocorrect="off"
spellcheck="false"
@input="setTransformOptionDraft(opt.id, $event.target.value)"
/>
</template>
<template v-else-if="opt.type === 'number'">
<input
:id="'opt-' + opt.id"
type="number"
class="transform-options-number"
:value="transformOptionsDraft[opt.id]"
:min="opt.min != null ? opt.min : null"
:max="opt.max != null ? opt.max : null"
:step="opt.step != null ? opt.step : 1"
inputmode="numeric"
@input="setTransformOptionDraft(opt.id, $event.target.value === '' ? opt.default : Number($event.target.value))"
/>
</template>
</div>
</div>
<div class="transform-options-panel-actions">
<button type="button" class="transform-options-btn transform-options-btn-secondary" @click="resetTransformOptionsToDefaults">
Reset defaults
</button>
<button type="button" class="transform-options-btn transform-options-btn-secondary" @click="closeTransformOptions">
Cancel
</button>
<button type="button" class="transform-options-btn transform-options-btn-primary" @click="commitTransformOptions">
Save
</button>
</div>
</div>
</div>
</template>
</div>