mirror of
https://github.com/elder-plinius/P4RS3LT0NGV3.git
synced 2026-04-29 07:05:57 +02:00
532 lines
37 KiB
HTML
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 & 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">×</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>
|