משתמש:מוטי בוט/codex-hello-world.js
מראה
לתשומת ליבך: לאחר הפרסום, ייתכן שיהיה צורך לנקות את זיכרון המטמון (cache) של הדפדפן כדי להבחין בשינויים.
- פיירפוקס / ספארי: להחזיק את המקש Shift בעת לחיצה על טעינה מחדש (Reload) או ללחוץ על צירוף המקשים Ctrl-F5 או Ctrl-R (במחשב מק: ⌘-R).
- גוגל כרום: ללחוץ על צירוף המקשים Ctrl-Shift-R (במחשב מק: ⌘-Shift-R).
- אדג': להחזיק את המקש Ctrl בעת לחיצה על רענן (Refresh) או ללחוץ על צירוף המקשים Ctrl-F5.
//<nowiki>
(function () {
var style = document.createElement("style");
style.innerHTML = `
#jwb-app-layout {
display: flex;
flex-direction: row;
min-height: 98vh;
box-sizing: border-box;
position: relative;
padding-bottom: 60px;
}
#jwb-main-area {
flex: 1 1 0;
display: flex;
flex-direction: column;
min-width: 0;
}
#jwb-preview {
flex: 0 0 auto;
background: #fff;
border-bottom: 1px solid #ddd;
padding: 1em 2em 0.5em 2em;
}
#jwb-center-area {
flex: 1 1 0;
display: flex;
flex-direction: column;
justify-content: flex-end;
min-height: 0;
}
#jwb-tabs-area {
flex: 0 0 auto;
min-height: 200px;
max-height: none;
overflow-y: visible;
background: #f8f8f8;
border-top: 1px solid #eee;
border-bottom: 1px solid #eee;
padding: 1em 2em;
display: grid;
}
#jwb-tabs-area .cdx-tabs__tab-content {
grid-area: 1 / 1 / 2 / 2;
}
#jwb-tabs-area .cdx-tabs__tab-content {
min-height: 0;
}
#jwb-tabs-area .cdx-tabs__tab-content {
/* כל הטאבים יתפסו את אותו שטח, הגבוהה ביותר תקבע */
position: relative;
}
#jwb-edit-area {
flex: 1 1 0;
display: flex;
flex-direction: column;
justify-content: flex-end;
min-height: 0;
}
#jwb-edit {
flex: 0 0 auto;
padding: 1em 2em;
background: #fafaff;
border-top: 1px solid #eee;
height: 320px;
max-height: 40vh;
min-height: 200px;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
#jwb-edit .cdx-text-area {
flex: 1 1 auto;
min-height: 0;
height: 100%;
max-height: 100%;
}
#jwb-controls {
display: flex;
gap: 0.5em;
align-items: center;
padding: 0.5em 2em;
background: #f4f4f4;
border-bottom: 1px solid #eee;
}
.jwb-controls-top {
border-bottom: none;
background: transparent;
padding-bottom: 0;
}
.jwb-controls-bottom {
border-bottom: 1px solid #eee;
background: #f4f4f4;
padding-bottom: 0.5em;
}
#jwb-pageslist-sidebar {
flex: 0 0 320px;
background: #f8f8f8;
border-left: 2px solid #ddd;
box-shadow: 2px 0 8px #0001;
display: flex;
flex-direction: column;
justify-content: flex-end;
min-width: 320px;
max-width: 320px;
padding: 0;
}
.pageslist-spacer {
flex: 1 1 auto;
}
#pagesList-container {
flex: 0 0 auto;
padding: 1em 0.5em 1em 0.5em;
max-height: 50vh;
overflow-y: auto;
}
#pagesList {
min-height: 120px;
max-height: 40vh;
width: 100%;
box-sizing: border-box;
resize: vertical;
}
#jwb-state {
position: fixed;
bottom: 0;
left: 0;
width: 97.5vw;
background: #eaeaea;
border-top: 1px solid #ccc;
padding: 0.5em 2em;
z-index: 200;
text-align: left;
font-size: 1.1em;
box-shadow: 0 -2px 8px #0001;
display: flex;
flex-direction: row;
align-items: center;
gap: 1em;
}
#jwb-state > * {
display: inline;
margin: 0;
}
/* Slide transition for tabs */
.slide-tabs-enter-active, .slide-tabs-leave-active {
transition: max-height 0.4s cubic-bezier(.4,0,.2,1), opacity 0.4s cubic-bezier(.4,0,.2,1);
overflow: hidden;
}
.slide-tabs-enter-from, .slide-tabs-leave-to {
max-height: 0;
opacity: 0;
}
.slide-tabs-enter-to, .slide-tabs-leave-from {
max-height: 1000px;
opacity: 1;
}
.cdx-label__description {
display: block;
}`;
document.head.appendChild(style);
})();
mw.loader.using(["vue", "@wikimedia/codex"], function (require) {
const Vue = require("vue");
const {
CdxTabs,
CdxTab,
CdxSelect,
CdxTextInput,
CdxButton,
CdxCheckbox,
CdxTextArea,
CdxRadio,
CdxField,
} = require("@wikimedia/codex");
const app = Vue.createApp({
name: "JWBInterface",
components: {
CdxTabs,
CdxTab,
CdxSelect,
CdxTextInput,
CdxButton,
CdxCheckbox,
CdxTextArea,
CdxRadio,
CdxField,
},
template: ` <div id="jwb-app-layout">
<div id="jwb-main-area">
<div id="jwb-preview">
<h3>תצוגה מקדימה</h3>
<div v-html="previewText"></div>
</div>
<div id="jwb-center-area">
<div v-if="!isRunning" id="jwb-controls" class="jwb-controls-top">
<cdx-button
v-for="btn in buttons"
:key="btn.key"
:action="btn.action"
:weight="btn.weight"
@click="btn.onClick"
>
{{ btn.label }}
</cdx-button>
</div>
<div v-if="!isRunning" id="jwb-tabs-area">
<cdx-tabs>
<cdx-tab name="general" label="כללי">
<!-- תוכן הטאב הכללי -->
<fieldset id="pagelist">
<legend>{{ "label-pagelist" }}</legend>
<cdx-button id="pagelistButton" @click="pagelistGenerate">
{{ "pagelist-generate" }}
</cdx-button>
<cdx-button id="removeDupes" @click="removeDupes">
{{ "button-remove-dupes" }}
</cdx-button>
<cdx-button id="sortArticles" @click="sortArticles">
{{ "button-sort" }}
</cdx-button>
</fieldset>
<fieldset id="limits">
<legend>{{ "label-limits" }}</legend>
<p>זמן חיפוש</p>
<cdx-text-input
v-model="limits.inputValueTimeLimit"
input-type="number"
id="timelimit"
min="0"
>timelimit-label</cdx-text-input
>
<p>מספר תווים מקסימלי</p>
<cdx-text-input
v-model="limits.inputValueSizeLimit"
input-type="number"
id="sizelimit"
min="0"
>diff-size-limit</cdx-text-input
>
</fieldset>
</cdx-tab>
<cdx-tab name="edit-setting" label="עריכה">
<!-- תוכן טאב אפשרויות העריכה -->
<fieldset id="save-setting">
<legend>{{ "label-save-setting" }}</legend>
<cdx-checkbox
v-model="editFields.checkboxValueEditMinor"
key="minor-edit"
>{{ "label-minor-edit" }}</cdx-checkbox
>
<cdx-checkbox
v-model="editFields.checkboxValueHasTag"
key="hasTag"
>{{ "label-hasTag" }}</cdx-checkbox
>
<cdx-text-input
v-model="editFields.inputTextSummary"
key="summary"
>{{ "label-summary" }}</cdx-text-input
>
<cdx-checkbox
v-model="editFields.checkboxValueAutoSave"
key="auto-save"
>{{ "auto save" }}</cdx-checkbox
>
<cdx-text-input
v-model="editFields.inputValueSaveInterval"
input-type="number"
id="save-interval"
>{{ "save-interval" }}</cdx-text-input
>
</fieldset>
<fieldset id="edit-options">
<legend>{{ "edit-options" }}</legend>
<cdx-field :is-fieldset="true">
<template #label> display completion </template>
<cdx-radio
v-for="radio in radiosDisplay"
:key="'radio-' + radio.value"
v-model="radioValueDisplay"
name="radio-group-descriptions"
:input-value="radio.value"
:inline="true"
>
{{ radio.label }}
<template #description>
{{ radio.description }}
</template>
</cdx-radio>
</cdx-field>
</fieldset>
<fieldset id="more-options">
<legend>{{ "more options" }}</legend>
<cdx-field :is-fieldset="true">
<template #label> redirects completion </template>
<cdx-radio
v-for="radio in radiosRedirects"
:key="'radio-' + radio.value"
v-model="radioValueRedirects"
name="radio-group-descriptions"
:input-value="radio.value"
:inline="true"
>
{{ radio.label }}
<template #description>
{{ radio.description }}
</template>
</cdx-radio>
</cdx-field>
</fieldset>
</cdx-tab>
<cdx-tab name="import-options" label="ייבוא">
<cdx-checkbox v-model="enableImport">
אפשר טעינה של ויקיטקסט מאתר חיצוני
</cdx-checkbox>
<fieldset :disabled="!enableImport" style="margin-top: 1em">
<legend>אפשרויות ייבוא</legend>
<cdx-field :is-fieldset="true">
<template #label> בחר מצב ייבוא </template>
<cdx-radio
v-for="mode in importModes"
:key="mode.value"
v-model="importMode"
:input-value="mode.value"
name="import-mode"
>
{{ mode.label }}
</cdx-radio>
</cdx-field>
<cdx-checkbox v-model="enableWikidata" style="margin-top: 1em">
שלב טעינה מוויקינתונים
</cdx-checkbox>
</fieldset>
</cdx-tab>
<cdx-tab name="replace-options" label="החלפות">
<fieldset id="replacements">
<legend>החלפות</legend>
<div
v-for="(replacement, idx) in replacements"
:key="idx"
style="
margin-bottom: 1em;
display: flex;
gap: 0.5em;
align-items: center;
"
>
<cdx-text-input
v-model="replacement.source"
placeholder="מקור"
style="width: 40%"
/>
<span>→</span>
<cdx-text-input
v-model="replacement.target"
placeholder="החלפה"
style="width: 40%"
/>
<cdx-button
@click="removeReplacement(idx)"
size="small"
style="margin-right: 0.5em"
>הסר</cdx-button
>
</div>
<cdx-button @click="addReplacement" size="small"
>הוסף החלפה</cdx-button
>
</fieldset>
</cdx-tab>
<cdx-tab name="skip-settings" label="הגדרות דילוג">
<fieldset id="skip-settings">
<legend>הגדרות דילוג</legend>
<cdx-checkbox v-model="skipSettings.skipIfEmpty">דלג אם הדף ריק</cdx-checkbox>
<cdx-checkbox v-model="skipSettings.skipNoChange">דלג אם לא בוצעו שינויים</cdx-checkbox>
<div style="margin: 0.5em 0;">
<span>דלג לפי קיום הדף:</span>
<cdx-radio v-model="skipSettings.existsOption" name="existsOption" input-value="any">כל מצב</cdx-radio>
<cdx-radio v-model="skipSettings.existsOption" name="existsOption" input-value="yes">רק אם קיים</cdx-radio>
<cdx-radio v-model="skipSettings.existsOption" name="existsOption" input-value="no">רק אם לא קיים</cdx-radio>
</div>
<cdx-text-input v-model="skipSettings.skipIfTitleContains" placeholder="דלג אם שם הדף מכיל..." />
<cdx-text-input v-model="skipSettings.skipIfTemplate" placeholder="דלג אם הדף מכיל תבנית..." />
<cdx-text-input v-model="skipSettings.skipContains" placeholder="דלג אם הדף מכיל מחרוזת..." />
<cdx-text-input v-model="skipSettings.skipNotContains" placeholder="דלג אם הדף לא מכיל מחרוזת..." />
<cdx-checkbox v-model="skipSettings.containRegex">השתמש בביטוי רגולרי</cdx-checkbox>
<cdx-text-input v-if="skipSettings.containRegex" v-model="skipSettings.containFlags" placeholder="דגלי regex (למשל: gi)" />
<cdx-text-input v-model="skipSettings.skipCategories" placeholder="דלג אם הדף בקטגוריה... (מופרד בפסיקים)" />
</fieldset>
</cdx-tab>
<cdx-tab v-if="isSysop" name="adminTab" label="כלים למפעילים">
<fieldset>
<legend>העברה</legend>
<cdx-checkbox v-model="admin.moveSuppressRedirect">דיכוי הפניה</cdx-checkbox>
<cdx-checkbox v-model="admin.moveTalk">העבר דף שיחה</cdx-checkbox>
<cdx-checkbox v-model="admin.moveSubpages">העבר דפי משנה</cdx-checkbox>
<cdx-text-input v-model="admin.moveTo" placeholder="שם חדש לדף" />
<cdx-button>העבר</cdx-button>
</fieldset>
<fieldset>
<legend>מחיקה</legend>
<cdx-checkbox v-model="admin.deleteTalk">מחק גם דף שיחה</cdx-checkbox>
<cdx-button>מחק</cdx-button>
</fieldset>
<fieldset>
<legend>הגנה</legend>
<div>עריכה:
<cdx-select v-model="admin.editProt" :options="admin.protOptions" />
</div>
<div>העברה:
<cdx-select v-model="admin.moveProt" :options="admin.protOptions" />
</div>
<div>העלאה:
<cdx-select v-model="admin.uploadProt" :options="admin.protOptions" />
</div>
<cdx-text-input v-model="admin.protectExpiry" placeholder="תוקף (למשל: infinite)" />
<cdx-button>הגן</cdx-button>
</fieldset>
</cdx-tab>
<!-- הוסף טאבים נוספים כנדרש -->
</cdx-tabs>
</div>
<div v-if="isRunning" id="jwb-edit-area">
<div id="jwb-edit">
<h3>עריכה</h3>
<cdx-text-area v-model="inputTextEdit" rows="10"></cdx-text-area>
</div>
<div id="jwb-controls" class="jwb-controls-bottom">
<cdx-button
v-for="btn in buttons"
:key="btn.key"
:action="btn.action"
:weight="btn.weight"
@click="btn.onClick"
>
{{ btn.label }}
</cdx-button>
</div>
</div>
</div>
</div>
<div id="jwb-pageslist-sidebar">
<div class="pageslist-spacer"></div>
<div id="pagesList-container">
<cdx-text-area
v-model="inputTextPagesList"
id="pagesList"
></cdx-text-area>
</div>
</div>
<div id="jwb-state">
<span
>בעריכה: <b>{{ currentPage }}</b></span
>
<span> | סטטיסטיקה: {{ stats }}</span>
</div>
</div>`,
setup() {
const isRunning = Vue.ref(false);
const currentPage = Vue.ref(1);
const stats = Vue.ref("0/0");
const radioValueDisplay = Vue.ref("diff");
const radioValueRedirects = Vue.ref("flow");
const enableImport = Vue.ref(false);
const importMode = Vue.ref("import-only");
const enableWikidata = Vue.ref(false);
const inputTextEdit = Vue.ref("");
const inputTextPagesList = Vue.ref("");
function startStop() {
isRunning.value = !isRunning.value;
}
function toggleDisplayMode() {
radioValueDisplay.value =
radioValueDisplay.value === "Preview" ? "diff" : "Preview";
}
function savePage() {
// כאן תבוא לוגיקת שמירה
}
function skipPage() {
// כאן תבוא לוגיקת דילוג
}
const importModes = [
{ value: "import-only", label: "ייבוא בלבד (יצירת דף חדש)" },
{ value: "update-only", label: "עדכון בלבד (עריכת דף קיים)" },
{ value: "both", label: "ייבוא ועדכון" },
];
const radiosDisplay = [
{
label: "diff mode",
description:
"Corrects up to two typos. Removes redirects that are very similar to the main title.",
value: "diff",
},
{
label: "Preview mode",
description:
"No typo correction. No accent folding. Strict matching.",
value: "Preview",
},
];
const radiosRedirects = [
{
label: "flow",
description: "flows",
value: "flow",
},
{
label: "skip",
description: "skip",
value: "skip",
},
{
label: "edit",
description: "edit",
value: "edit",
},
];
const buttons = Vue.computed(() => [
{
key: "startStop",
label: isRunning.value ? "עצירה" : "הפעלה",
action: isRunning.value ? "destructive" : "progressive",
weight: "primary",
onClick: startStop,
},
{
key: "toggleDisplay",
label:
radioValueDisplay.value === "Preview"
? "הצגת הבדלים"
: "תצוגה מקדימה",
action: "normal",
onClick: toggleDisplayMode,
},
{
key: "save",
label: "שמירה",
action: "progressive",
onClick: savePage,
},
{
key: "skip",
label: "דילוג",
action: "destructive",
onClick: skipPage,
},
]);
const editFields = Vue.reactive({
inputTextSummary: "",
inputValueSaveInterval: "",
checkboxValueEditMinor: false,
checkboxValueHasTag: false,
checkboxValueAutoSave: false,
});
// קיבוץ שדות מגבלות
const limits = Vue.reactive({
inputValueTimeLimit: "",
inputValueSizeLimit: "",
});
// החלפות
const replacements = Vue.reactive([{ source: "", target: "" }]);
function addReplacement() {
replacements.push({ source: "", target: "" });
}
function removeReplacement(idx) {
if (replacements.length > 1) {
replacements.splice(idx, 1);
} else {
replacements[0].source = "";
replacements[0].target = "";
}
}
const skipSettings = Vue.reactive({
skipIfEmpty: false,
skipNoChange: false,
existsOption: "any", // any/yes/no
skipIfTitleContains: "",
skipIfTemplate: "",
skipContains: "",
skipNotContains: "",
containRegex: false,
containFlags: "",
skipCategories: "",
});
const isSysop = Vue.ref(false); // יש להחליף בזיהוי אמיתי של הרשאות
const admin = Vue.reactive({
moveSuppressRedirect: false,
moveTalk: false,
moveSubpages: false,
moveTo: "",
deleteTalk: false,
editProt: "",
moveProt: "",
uploadProt: "",
protectExpiry: "",
protOptions: [
{ value: "all", label: "ללא הגנה" },
{ value: "autoconfirmed", label: "מאומתים" },
{ value: "sysop", label: "מפעילים" },
],
});
return {
isRunning,
radioValueDisplay,
radioValueRedirects,
radiosDisplay,
radiosRedirects,
enableImport,
importMode,
enableWikidata,
importModes,
inputTextEdit,
inputTextPagesList,
editFields,
limits,
replacements,
addReplacement,
removeReplacement,
savePage,
skipPage,
toggleDisplayMode,
startStop,
currentPage,
stats,
buttons,
skipSettings,
isSysop,
admin,
};
},
});
const jwbContainer = document.createElement("div");
jwbContainer.id = "jwb-container";
document.body.innerHTML = "";
document.body.appendChild(jwbContainer);
app.mount("#jwb-container");
});
//</nowiki>