syntaxmatrix 2.6.4.4__py3-none-any.whl → 3.0.1__py3-none-any.whl
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.
- syntaxmatrix/__init__.py +6 -4
- syntaxmatrix/agentic/agents.py +206 -26
- syntaxmatrix/agentic/agents_orchestrer.py +16 -10
- syntaxmatrix/client_docs.py +237 -0
- syntaxmatrix/commentary.py +96 -25
- syntaxmatrix/core.py +142 -56
- syntaxmatrix/dataset_preprocessing.py +2 -2
- syntaxmatrix/db.py +0 -17
- syntaxmatrix/kernel_manager.py +174 -150
- syntaxmatrix/page_builder_generation.py +656 -63
- syntaxmatrix/page_layout_contract.py +25 -3
- syntaxmatrix/page_patch_publish.py +368 -15
- syntaxmatrix/plugins/__init__.py +0 -0
- syntaxmatrix/premium/__init__.py +10 -2
- syntaxmatrix/premium/catalogue/__init__.py +121 -0
- syntaxmatrix/premium/gate.py +15 -3
- syntaxmatrix/premium/state.py +507 -0
- syntaxmatrix/premium/verify.py +222 -0
- syntaxmatrix/profiles.py +1 -1
- syntaxmatrix/routes.py +9847 -8004
- syntaxmatrix/settings/model_map.py +50 -65
- syntaxmatrix/settings/prompts.py +1186 -414
- syntaxmatrix/settings/string_navbar.py +4 -4
- syntaxmatrix/static/icons/bot_icon.png +0 -0
- syntaxmatrix/static/icons/bot_icon2.png +0 -0
- syntaxmatrix/templates/admin_billing.html +408 -0
- syntaxmatrix/templates/admin_branding.html +65 -2
- syntaxmatrix/templates/admin_features.html +54 -0
- syntaxmatrix/templates/dashboard.html +285 -8
- syntaxmatrix/templates/edit_page.html +199 -18
- syntaxmatrix/themes.py +17 -17
- syntaxmatrix/workspace_db.py +0 -23
- syntaxmatrix-3.0.1.dist-info/METADATA +219 -0
- {syntaxmatrix-2.6.4.4.dist-info → syntaxmatrix-3.0.1.dist-info}/RECORD +38 -33
- {syntaxmatrix-2.6.4.4.dist-info → syntaxmatrix-3.0.1.dist-info}/WHEEL +1 -1
- syntaxmatrix/settings/default.yaml +0 -13
- syntaxmatrix-2.6.4.4.dist-info/METADATA +0 -539
- syntaxmatrix-2.6.4.4.dist-info/licenses/LICENSE.txt +0 -21
- /syntaxmatrix/{plugin_manager.py → plugins/plugin_manager.py} +0 -0
- /syntaxmatrix/static/icons/{logo3.png → logo2.png} +0 -0
- {syntaxmatrix-2.6.4.4.dist-info → syntaxmatrix-3.0.1.dist-info}/top_level.txt +0 -0
|
@@ -703,6 +703,116 @@
|
|
|
703
703
|
font-size: 0.8em;
|
|
704
704
|
opacity: 0.7;
|
|
705
705
|
}
|
|
706
|
+
|
|
707
|
+
/* ----------------------------
|
|
708
|
+
Thought Process renderer
|
|
709
|
+
---------------------------- */
|
|
710
|
+
.tp-render{margin-top:10px}
|
|
711
|
+
.tp-grid{
|
|
712
|
+
display:grid;
|
|
713
|
+
grid-template-columns: 1fr 1fr;
|
|
714
|
+
gap:10px;
|
|
715
|
+
margin-bottom:10px;
|
|
716
|
+
}
|
|
717
|
+
@media (max-width: 900px){
|
|
718
|
+
.tp-grid{grid-template-columns: 1fr;}
|
|
719
|
+
}
|
|
720
|
+
.tp-card{
|
|
721
|
+
border:1px solid var(--border);
|
|
722
|
+
border-radius: 12px;
|
|
723
|
+
background: #fff;
|
|
724
|
+
padding:10px 12px;
|
|
725
|
+
overflow:hidden;
|
|
726
|
+
}
|
|
727
|
+
.tp-card h4{
|
|
728
|
+
margin:0 0 6px;
|
|
729
|
+
font-size:.95rem;
|
|
730
|
+
letter-spacing:.01em;
|
|
731
|
+
}
|
|
732
|
+
.tp-kv{
|
|
733
|
+
display:grid;
|
|
734
|
+
grid-template-columns: 180px 1fr;
|
|
735
|
+
gap:6px 10px;
|
|
736
|
+
font-size:.92rem;
|
|
737
|
+
line-height:1.35;
|
|
738
|
+
}
|
|
739
|
+
.tp-kv .k{color: var(--muted); font-weight:700;}
|
|
740
|
+
.tp-kv .v{color: var(--text);}
|
|
741
|
+
.tp-steps ol{
|
|
742
|
+
margin:0;
|
|
743
|
+
padding-left: 1.1rem;
|
|
744
|
+
}
|
|
745
|
+
.tp-steps li{ margin: 0 0 6px; }
|
|
746
|
+
|
|
747
|
+
.tp-code{ position:relative; }
|
|
748
|
+
.tp-code pre{
|
|
749
|
+
margin:0;
|
|
750
|
+
padding:10px 12px;
|
|
751
|
+
border-radius:12px;
|
|
752
|
+
background: #0b1220;
|
|
753
|
+
color:#e5e7eb;
|
|
754
|
+
overflow:auto;
|
|
755
|
+
border:1px solid rgba(15,23,42,.2);
|
|
756
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
757
|
+
font-size: .86rem;
|
|
758
|
+
line-height: 1.45;
|
|
759
|
+
white-space: pre;
|
|
760
|
+
}
|
|
761
|
+
.tp-copy{
|
|
762
|
+
position:absolute;
|
|
763
|
+
top:10px;
|
|
764
|
+
right:10px;
|
|
765
|
+
border:1px solid var(--border);
|
|
766
|
+
background:#fff;
|
|
767
|
+
color:var(--text);
|
|
768
|
+
border-radius:999px;
|
|
769
|
+
padding:5px 10px;
|
|
770
|
+
font-weight:700;
|
|
771
|
+
cursor:pointer;
|
|
772
|
+
font-size:.78rem;
|
|
773
|
+
}
|
|
774
|
+
.tp-copy:hover{filter:brightness(.98)}
|
|
775
|
+
|
|
776
|
+
.tp-badges{
|
|
777
|
+
display:flex;
|
|
778
|
+
flex-wrap:wrap;
|
|
779
|
+
gap:8px;
|
|
780
|
+
margin-bottom:10px;
|
|
781
|
+
}
|
|
782
|
+
.tp-badge{
|
|
783
|
+
border:1px solid var(--border);
|
|
784
|
+
border-radius:999px;
|
|
785
|
+
padding:4px 10px;
|
|
786
|
+
background:#fff;
|
|
787
|
+
font-size:.8rem;
|
|
788
|
+
font-weight:700;
|
|
789
|
+
color: var(--text);
|
|
790
|
+
}
|
|
791
|
+
|
|
792
|
+
.tp-raw{
|
|
793
|
+
margin-top:10px;
|
|
794
|
+
border:1px dashed var(--border);
|
|
795
|
+
border-radius:12px;
|
|
796
|
+
padding:8px 10px;
|
|
797
|
+
background:#fff;
|
|
798
|
+
}
|
|
799
|
+
.tp-raw summary{
|
|
800
|
+
cursor:pointer;
|
|
801
|
+
font-weight:800;
|
|
802
|
+
color: var(--muted);
|
|
803
|
+
}
|
|
804
|
+
.tp-raw-pre{
|
|
805
|
+
margin:10px 0 0;
|
|
806
|
+
padding:10px 12px;
|
|
807
|
+
border-radius:10px;
|
|
808
|
+
background:#f8fafc;
|
|
809
|
+
border:1px solid var(--border);
|
|
810
|
+
overflow:auto;
|
|
811
|
+
max-height: 320px;
|
|
812
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
|
813
|
+
font-size: .85rem;
|
|
814
|
+
white-space: pre-wrap;
|
|
815
|
+
}
|
|
706
816
|
</style>
|
|
707
817
|
|
|
708
818
|
<script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
|
|
@@ -781,7 +891,6 @@
|
|
|
781
891
|
<textarea id="askai" name="askai_question" type="text" rows="5"
|
|
782
892
|
style="position:relative; width:90%; padding:16px; font-size:0.8em; border-radius:8px;"
|
|
783
893
|
placeholder="Ask me about {{ (selected_dataset or 'your dataset\n. But upload it first.').replace('_', ' ').replace('.csv', '') }}" required></textarea>
|
|
784
|
-
<!-- <button type="submit" style="font-size:1.2rem; width:8rem; padding:4px;">Submit</button> -->
|
|
785
894
|
<button
|
|
786
895
|
type="submit"
|
|
787
896
|
class="btn btn-primary eda-submit-btn"
|
|
@@ -798,21 +907,25 @@
|
|
|
798
907
|
</form>
|
|
799
908
|
<div style="margin-bottom: 36px;">
|
|
800
909
|
{% if askai_question %}
|
|
801
|
-
<!--
|
|
802
910
|
<div class="askai-qblock" style="margin:20px 5px;">
|
|
803
|
-
<span class="askai-q-label"><b>
|
|
911
|
+
<span class="askai-q-label"><b>Question:</b></span><br>
|
|
804
912
|
<span class="askai-q">{{ askai_question }}</span>
|
|
805
913
|
</div>
|
|
806
|
-
|
|
807
|
-
<br><br>
|
|
914
|
+
<hr><br>
|
|
808
915
|
<div class="refined-qblock">
|
|
809
916
|
<details>
|
|
810
917
|
<summary class="refined-q-label" style="cursor: pointer; list-style: none;">
|
|
811
918
|
<span class="toggle-arrow">▶</span>
|
|
812
|
-
<b>
|
|
919
|
+
<b>Reasoning Process</b>
|
|
813
920
|
</summary>
|
|
814
921
|
<div class="li" style="margin-top: 10px; padding-left: 14px; border-left: 3px solid #e0e5ee;">
|
|
815
|
-
<
|
|
922
|
+
<div class="tp-render" id="tp-render"></div>
|
|
923
|
+
<script type="application/json" id="tp-data">{{ (thought_obj if thought_obj else refined_question)|tojson }}</script>
|
|
924
|
+
|
|
925
|
+
<details class="tp-raw">
|
|
926
|
+
<pre class="tp-raw-pre">{{ refined_question|e }}</pre>
|
|
927
|
+
</details>
|
|
928
|
+
<hl>
|
|
816
929
|
<br><br>
|
|
817
930
|
{% if tasks %}
|
|
818
931
|
<b>Tasks Performed: </b>
|
|
@@ -822,7 +935,7 @@
|
|
|
822
935
|
{% endif %}
|
|
823
936
|
<br><br>
|
|
824
937
|
{% if TOKENS %}
|
|
825
|
-
<b>
|
|
938
|
+
<b>Assistant Agent: </b><span>{{ TOKENS['Refiner'][0] }} | {{ TOKENS['Refiner'][1] }}</span><br>
|
|
826
939
|
<b>Token Usage: </b>
|
|
827
940
|
<li>Input Tokens: {{ TOKENS['Refiner'][2] }}</li>
|
|
828
941
|
<li>Output Tokens: {{ TOKENS['Refiner'][3] }}</li>
|
|
@@ -839,6 +952,7 @@
|
|
|
839
952
|
</details>
|
|
840
953
|
</div>
|
|
841
954
|
{% endif %}
|
|
955
|
+
<hr>
|
|
842
956
|
{% if ai_outputs %}
|
|
843
957
|
<div class="d-flex align-items-center justify-content-between" style="margin: 12px;">
|
|
844
958
|
<br>
|
|
@@ -1012,5 +1126,168 @@
|
|
|
1012
1126
|
}
|
|
1013
1127
|
});
|
|
1014
1128
|
</script>
|
|
1129
|
+
<script>
|
|
1130
|
+
(function(){
|
|
1131
|
+
const host = document.getElementById("tp-render");
|
|
1132
|
+
const src = document.getElementById("tp-data");
|
|
1133
|
+
if (!host || !src) return;
|
|
1134
|
+
|
|
1135
|
+
function safeString(x){
|
|
1136
|
+
if (x === null || x === undefined) return "";
|
|
1137
|
+
if (typeof x === "string") return x;
|
|
1138
|
+
try { return JSON.stringify(x, null, 2); } catch(e){ return String(x); }
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
function el(tag, cls){
|
|
1142
|
+
const e = document.createElement(tag);
|
|
1143
|
+
if (cls) e.className = cls;
|
|
1144
|
+
return e;
|
|
1145
|
+
}
|
|
1146
|
+
|
|
1147
|
+
let data = null;
|
|
1148
|
+
try{
|
|
1149
|
+
data = JSON.parse(src.textContent || "null");
|
|
1150
|
+
}catch(e){
|
|
1151
|
+
data = (src.textContent || "").trim();
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
// If the payload is itself a JSON string, try parse again.
|
|
1155
|
+
if (typeof data === "string"){
|
|
1156
|
+
const s = data.trim();
|
|
1157
|
+
if ((s.startsWith("{") && s.endsWith("}")) || (s.startsWith("[") && s.endsWith("]"))){
|
|
1158
|
+
try { data = JSON.parse(s); } catch(e) {}
|
|
1159
|
+
}
|
|
1160
|
+
}
|
|
1161
|
+
|
|
1162
|
+
// Fallback: render as plain pre.
|
|
1163
|
+
if (!data || (typeof data !== "object")){
|
|
1164
|
+
const pre = el("pre", "tp-raw-pre");
|
|
1165
|
+
pre.textContent = safeString(data);
|
|
1166
|
+
host.appendChild(pre);
|
|
1167
|
+
return;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
const taskCat = data["ML_Task_Category"] || data["Task_Category"] || data["Task category"] || "";
|
|
1171
|
+
const statTest = (data["Logic Mapping"] && (data["Logic Mapping"]["Statistical Test"] || data["Logic Mapping"]["statistical_test"])) ||
|
|
1172
|
+
data["Statistical Test"] || data["Statistical_Test"] || "";
|
|
1173
|
+
const viz = data["Visualization Template"] || data["Visualisation Template"] || data["Visualization"] || data["Visualisation"] || "";
|
|
1174
|
+
|
|
1175
|
+
const badges = el("div", "tp-badges");
|
|
1176
|
+
function addBadge(label, value){
|
|
1177
|
+
if (!value) return;
|
|
1178
|
+
const b = el("div", "tp-badge");
|
|
1179
|
+
b.textContent = label + ": " + value;
|
|
1180
|
+
badges.appendChild(b);
|
|
1181
|
+
}
|
|
1182
|
+
addBadge("Category", safeString(taskCat));
|
|
1183
|
+
addBadge("Test", safeString(statTest));
|
|
1184
|
+
addBadge("Visual", safeString(viz));
|
|
1185
|
+
if (badges.childElementCount) host.appendChild(badges);
|
|
1186
|
+
|
|
1187
|
+
const grid = el("div", "tp-grid");
|
|
1188
|
+
|
|
1189
|
+
// Logic mapping
|
|
1190
|
+
const mapping = data["Logic Mapping"] || data["Logic_Mapping"] || null;
|
|
1191
|
+
const mapCard = el("div", "tp-card");
|
|
1192
|
+
const mapTitle = el("h4"); mapTitle.textContent = "Logic mapping";
|
|
1193
|
+
mapCard.appendChild(mapTitle);
|
|
1194
|
+
|
|
1195
|
+
const kv = el("div", "tp-kv");
|
|
1196
|
+
if (mapping && typeof mapping === "object"){
|
|
1197
|
+
Object.keys(mapping).forEach(k=>{
|
|
1198
|
+
const v = mapping[k];
|
|
1199
|
+
const kEl = el("div","k"); kEl.textContent = k;
|
|
1200
|
+
const vEl = el("div","v"); vEl.textContent = safeString(v);
|
|
1201
|
+
kv.appendChild(kEl); kv.appendChild(vEl);
|
|
1202
|
+
});
|
|
1203
|
+
} else {
|
|
1204
|
+
const kEl = el("div","k"); kEl.textContent = "Mapping";
|
|
1205
|
+
const vEl = el("div","v"); vEl.textContent = "—";
|
|
1206
|
+
kv.appendChild(kEl); kv.appendChild(vEl);
|
|
1207
|
+
}
|
|
1208
|
+
mapCard.appendChild(kv);
|
|
1209
|
+
grid.appendChild(mapCard);
|
|
1210
|
+
|
|
1211
|
+
// Steps
|
|
1212
|
+
const stepsRaw = data["Step-by-Step Chain-of-Thought"] || data["Step-by-Step"] || data["Steps"] || data["Plan"] || null;
|
|
1213
|
+
const stepsCard = el("div", "tp-card tp-steps");
|
|
1214
|
+
const stepsTitle = el("h4"); stepsTitle.textContent = "Steps";
|
|
1215
|
+
stepsCard.appendChild(stepsTitle);
|
|
1216
|
+
|
|
1217
|
+
const ol = el("ol");
|
|
1218
|
+
let steps = [];
|
|
1219
|
+
if (Array.isArray(stepsRaw)) {
|
|
1220
|
+
steps = stepsRaw.map(x=>safeString(x)).filter(Boolean);
|
|
1221
|
+
} else if (typeof stepsRaw === "string") {
|
|
1222
|
+
steps = stepsRaw.split(/\n+/).map(s=>s.trim()).filter(Boolean);
|
|
1223
|
+
}
|
|
1224
|
+
if (!steps.length) {
|
|
1225
|
+
const li = el("li"); li.textContent = "—";
|
|
1226
|
+
ol.appendChild(li);
|
|
1227
|
+
} else {
|
|
1228
|
+
steps.forEach(s=>{
|
|
1229
|
+
const li = el("li"); li.textContent = s.replace(/^\d+\.\s*/, "");
|
|
1230
|
+
ol.appendChild(li);
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
stepsCard.appendChild(ol);
|
|
1234
|
+
grid.appendChild(stepsCard);
|
|
1235
|
+
|
|
1236
|
+
host.appendChild(grid);
|
|
1237
|
+
|
|
1238
|
+
// Code snippet
|
|
1239
|
+
const code = data["Code Snippet"] || data["Code"] || data["code"] || "";
|
|
1240
|
+
if (code){
|
|
1241
|
+
const codeCard = el("div", "tp-card tp-code");
|
|
1242
|
+
const h = el("h4"); h.textContent = "Generated code";
|
|
1243
|
+
codeCard.appendChild(h);
|
|
1244
|
+
|
|
1245
|
+
const btn = el("button", "tp-copy");
|
|
1246
|
+
btn.type = "button";
|
|
1247
|
+
btn.textContent = "Copy";
|
|
1248
|
+
btn.addEventListener("click", async ()=>{
|
|
1249
|
+
try{
|
|
1250
|
+
await navigator.clipboard.writeText(String(code));
|
|
1251
|
+
btn.textContent = "Copied";
|
|
1252
|
+
setTimeout(()=> btn.textContent = "Copy", 900);
|
|
1253
|
+
}catch(e){
|
|
1254
|
+
btn.textContent = "Copy failed";
|
|
1255
|
+
setTimeout(()=> btn.textContent = "Copy", 1100);
|
|
1256
|
+
}
|
|
1257
|
+
});
|
|
1258
|
+
codeCard.appendChild(btn);
|
|
1259
|
+
|
|
1260
|
+
const pre = el("pre");
|
|
1261
|
+
pre.textContent = String(code);
|
|
1262
|
+
codeCard.appendChild(pre);
|
|
1263
|
+
|
|
1264
|
+
host.appendChild(codeCard);
|
|
1265
|
+
}
|
|
1266
|
+
|
|
1267
|
+
// Any other keys
|
|
1268
|
+
const known = new Set([
|
|
1269
|
+
"ML_Task_Category","Task_Category","Task category",
|
|
1270
|
+
"Logic Mapping","Logic_Mapping",
|
|
1271
|
+
"Visualization Template","Visualisation Template","Visualization","Visualisation",
|
|
1272
|
+
"Step-by-Step Chain-of-Thought","Step-by-Step","Steps","Plan",
|
|
1273
|
+
"Code Snippet","Code","code"
|
|
1274
|
+
]);
|
|
1275
|
+
|
|
1276
|
+
const extraKeys = Object.keys(data).filter(k => !known.has(k));
|
|
1277
|
+
if (extraKeys.length){
|
|
1278
|
+
const extra = el("div","tp-card");
|
|
1279
|
+
const h = el("h4"); h.textContent = "Extra details";
|
|
1280
|
+
extra.appendChild(h);
|
|
1281
|
+
const kv2 = el("div","tp-kv");
|
|
1282
|
+
extraKeys.forEach(k=>{
|
|
1283
|
+
const kEl = el("div","k"); kEl.textContent = k;
|
|
1284
|
+
const vEl = el("div","v"); vEl.textContent = safeString(data[k]);
|
|
1285
|
+
kv2.appendChild(kEl); kv2.appendChild(vEl);
|
|
1286
|
+
});
|
|
1287
|
+
extra.appendChild(kv2);
|
|
1288
|
+
host.appendChild(extra);
|
|
1289
|
+
}
|
|
1290
|
+
})();
|
|
1291
|
+
</script>
|
|
1015
1292
|
</body>
|
|
1016
1293
|
</html>
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
<!-- SortableJS (drag & drop) -->
|
|
9
9
|
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
|
|
10
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/tinymce/6.8.3/tinymce.min.js" referrerpolicy="no-referrer"></script>
|
|
10
11
|
|
|
11
12
|
<!-- CodeMirror (real code editor: syntax colours + matching brackets) -->
|
|
12
13
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.16/codemirror.min.css"/>
|
|
@@ -279,9 +280,27 @@
|
|
|
279
280
|
background: rgba(2,6,23,.45);
|
|
280
281
|
transition: opacity .12s ease;
|
|
281
282
|
}
|
|
282
|
-
.item
|
|
283
|
-
|
|
284
|
-
|
|
283
|
+
.item .title{
|
|
284
|
+
font-weight:750;
|
|
285
|
+
font-size:.86rem;
|
|
286
|
+
line-height:1.25;
|
|
287
|
+
|
|
288
|
+
/* 2-line clamp so long titles look good */
|
|
289
|
+
display:-webkit-box;
|
|
290
|
+
-webkit-line-clamp:2;
|
|
291
|
+
-webkit-box-orient:vertical;
|
|
292
|
+
overflow:hidden;
|
|
293
|
+
min-height: calc(1.25em * 2);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.item .text{
|
|
297
|
+
opacity:.9;
|
|
298
|
+
font-size:.82rem;
|
|
299
|
+
color: var(--mut);
|
|
300
|
+
|
|
301
|
+
/* show new lines in the builder preview */
|
|
302
|
+
white-space: pre-wrap;
|
|
303
|
+
}
|
|
285
304
|
|
|
286
305
|
.item .cta-drop{
|
|
287
306
|
margin-top:6px;
|
|
@@ -555,7 +574,6 @@
|
|
|
555
574
|
<option value="4">Default: 4 cols</option>
|
|
556
575
|
<option value="5">Default: 5 cols</option>
|
|
557
576
|
</select>
|
|
558
|
-
<button class="btn danger" id="btn-clear" type="button">Clear</button>
|
|
559
577
|
</div>
|
|
560
578
|
</div>
|
|
561
579
|
<div class="sections" id="sections"></div>
|
|
@@ -586,9 +604,44 @@
|
|
|
586
604
|
<input id="insp-title" placeholder="Title"/>
|
|
587
605
|
</div>
|
|
588
606
|
|
|
589
|
-
|
|
607
|
+
<!-- Alignment controls (per item) -->
|
|
608
|
+
<div class="row" id="row-text-style" style="display:none;">
|
|
609
|
+
<div class="field" style="flex:1;">
|
|
610
|
+
<label>Title alignment</label>
|
|
611
|
+
<select id="insp-title-align">
|
|
612
|
+
<option value="">Default</option>
|
|
613
|
+
<option value="left">Left</option>
|
|
614
|
+
<option value="center">Centre</option>
|
|
615
|
+
<option value="right">Right</option>
|
|
616
|
+
<option value="justify">Justify</option>
|
|
617
|
+
</select>
|
|
618
|
+
</div>
|
|
619
|
+
|
|
620
|
+
<div class="field" style="flex:1;">
|
|
621
|
+
<label>Body alignment</label>
|
|
622
|
+
<select id="insp-text-align">
|
|
623
|
+
<option value="">Default</option>
|
|
624
|
+
<option value="left">Left</option>
|
|
625
|
+
<option value="center">Centre</option>
|
|
626
|
+
<option value="right">Right</option>
|
|
627
|
+
<option value="justify">Justify</option>
|
|
628
|
+
</select>
|
|
629
|
+
</div>
|
|
630
|
+
</div>
|
|
631
|
+
|
|
632
|
+
<!-- Plain text (used for sections + fallback) -->
|
|
633
|
+
<div class="field" id="field-text-plain">
|
|
590
634
|
<label>Text</label>
|
|
591
|
-
<textarea id="insp-text" placeholder="
|
|
635
|
+
<textarea id="insp-text-plain" rows="6" placeholder="Short description (new lines supported)"></textarea>
|
|
636
|
+
</div>
|
|
637
|
+
|
|
638
|
+
<!-- Rich text (WordPress-like) -->
|
|
639
|
+
<div class="field" id="field-text-rich" style="display:none;">
|
|
640
|
+
<label>Body</label>
|
|
641
|
+
<textarea id="insp-text-rich"></textarea>
|
|
642
|
+
<div class="muted" style="margin-top:6px;">
|
|
643
|
+
Tip: use the toolbar for bold, lists, colours, font sizes, alignment, and divider lines.
|
|
644
|
+
</div>
|
|
592
645
|
</div>
|
|
593
646
|
|
|
594
647
|
<div class="row">
|
|
@@ -1569,8 +1622,18 @@
|
|
|
1569
1622
|
const inspBox = document.getElementById("insp");
|
|
1570
1623
|
const inspEmpty = document.getElementById("insp-empty");
|
|
1571
1624
|
const inspKind = document.getElementById("insp-kind");
|
|
1625
|
+
|
|
1572
1626
|
const inspTitle = document.getElementById("insp-title");
|
|
1573
|
-
|
|
1627
|
+
// plain + rich
|
|
1628
|
+
const inspTextPlain = document.getElementById("insp-text-plain");
|
|
1629
|
+
const inspTextRich = document.getElementById("insp-text-rich");
|
|
1630
|
+
const fieldTextPlain = document.getElementById("field-text-plain");
|
|
1631
|
+
const fieldTextRich = document.getElementById("field-text-rich");
|
|
1632
|
+
// alignment
|
|
1633
|
+
const rowTextStyle = document.getElementById("row-text-style");
|
|
1634
|
+
const inspTitleAlign = document.getElementById("insp-title-align");
|
|
1635
|
+
const inspTextAlign = document.getElementById("insp-text-align");
|
|
1636
|
+
|
|
1574
1637
|
const inspCols = document.getElementById("insp-cols");
|
|
1575
1638
|
const inspImg = document.getElementById("insp-img");
|
|
1576
1639
|
const inspHref = document.getElementById("insp-href");
|
|
@@ -1597,6 +1660,83 @@
|
|
|
1597
1660
|
const secPad = document.getElementById("sec-pad");
|
|
1598
1661
|
const secAlign = document.getElementById("sec-align");
|
|
1599
1662
|
|
|
1663
|
+
function escapeHtml(s){
|
|
1664
|
+
s = String(s || "");
|
|
1665
|
+
return s
|
|
1666
|
+
.replaceAll("&", "&")
|
|
1667
|
+
.replaceAll("<", "<")
|
|
1668
|
+
.replaceAll(">", ">")
|
|
1669
|
+
.replaceAll('"', """)
|
|
1670
|
+
.replaceAll("'", "'");
|
|
1671
|
+
}
|
|
1672
|
+
|
|
1673
|
+
function plainToHtml(s){
|
|
1674
|
+
// turn new lines into <br> (good enough as a fallback)
|
|
1675
|
+
const esc = escapeHtml(s || "");
|
|
1676
|
+
return esc.replaceAll("\n", "<br>");
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
function htmlToText(html){
|
|
1680
|
+
if (!html) return "";
|
|
1681
|
+
const tmp = document.createElement("div");
|
|
1682
|
+
tmp.innerHTML = html;
|
|
1683
|
+
const txt = (tmp.textContent || tmp.innerText || "");
|
|
1684
|
+
return txt.replace(/\n{3,}/g, "\n\n").trim();
|
|
1685
|
+
}
|
|
1686
|
+
|
|
1687
|
+
let _tinymceInitDone = false;
|
|
1688
|
+
|
|
1689
|
+
function ensureTinyMCE(){
|
|
1690
|
+
if (_tinymceInitDone) return true;
|
|
1691
|
+
if (!window.tinymce) return false;
|
|
1692
|
+
|
|
1693
|
+
try{
|
|
1694
|
+
window.tinymce.init({
|
|
1695
|
+
selector: "#insp-text-rich",
|
|
1696
|
+
height: 280,
|
|
1697
|
+
menubar: false,
|
|
1698
|
+
branding: false,
|
|
1699
|
+
statusbar: false,
|
|
1700
|
+
plugins: "lists link hr autoresize",
|
|
1701
|
+
toolbar: "undo redo | bold italic underline | fontsize forecolor | alignleft aligncenter alignright alignjustify | bullist numlist | hr | link | removeformat",
|
|
1702
|
+
setup: (ed) => {
|
|
1703
|
+
ed.on("init", () => {
|
|
1704
|
+
_tinymceInitDone = true;
|
|
1705
|
+
if (ed._smxPendingHtml != null){
|
|
1706
|
+
ed.setContent(ed._smxPendingHtml);
|
|
1707
|
+
ed._smxPendingHtml = null;
|
|
1708
|
+
}
|
|
1709
|
+
});
|
|
1710
|
+
}
|
|
1711
|
+
});
|
|
1712
|
+
|
|
1713
|
+
return true;
|
|
1714
|
+
}catch(e){
|
|
1715
|
+
console.warn("TinyMCE init failed:", e);
|
|
1716
|
+
return false;
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
function setRichHtml(html){
|
|
1721
|
+
const ed = window.tinymce && window.tinymce.get("insp-text-rich");
|
|
1722
|
+
if (ed && _tinymceInitDone){
|
|
1723
|
+
ed.setContent(html || "");
|
|
1724
|
+
return;
|
|
1725
|
+
}
|
|
1726
|
+
if (ed){
|
|
1727
|
+
ed._smxPendingHtml = html || "";
|
|
1728
|
+
return;
|
|
1729
|
+
}
|
|
1730
|
+
inspTextRich.value = html || "";
|
|
1731
|
+
}
|
|
1732
|
+
|
|
1733
|
+
function getRichHtml(){
|
|
1734
|
+
const ed = window.tinymce && window.tinymce.get("insp-text-rich");
|
|
1735
|
+
if (ed && _tinymceInitDone){
|
|
1736
|
+
return ed.getContent() || "";
|
|
1737
|
+
}
|
|
1738
|
+
return inspTextRich.value || "";
|
|
1739
|
+
}
|
|
1600
1740
|
|
|
1601
1741
|
function openInspectorForSelection(){
|
|
1602
1742
|
if (heroCtasBox) heroCtasBox.style.display = "none";
|
|
@@ -1614,7 +1754,12 @@
|
|
|
1614
1754
|
|
|
1615
1755
|
inspKind.value = "Section · " + sectionLabel(s.type);
|
|
1616
1756
|
inspTitle.value = safeText(s.title || "");
|
|
1617
|
-
|
|
1757
|
+
if (rowTextStyle) rowTextStyle.style.display = "none";
|
|
1758
|
+
if (fieldTextPlain) fieldTextPlain.style.display = "block";
|
|
1759
|
+
if (fieldTextRich) fieldTextRich.style.display = "none";
|
|
1760
|
+
|
|
1761
|
+
inspTextPlain.value = safeText(s.text || "");
|
|
1762
|
+
|
|
1618
1763
|
inspCols.value = String(s.cols || 1);
|
|
1619
1764
|
inspImg.value = safeText(s.imageUrl || "");
|
|
1620
1765
|
|
|
@@ -1684,7 +1829,32 @@
|
|
|
1684
1829
|
|
|
1685
1830
|
inspKind.value = "Item · " + safeText(it.type);
|
|
1686
1831
|
inspTitle.value = safeText(it.title || "");
|
|
1687
|
-
|
|
1832
|
+
const useRich = (t !== "button"); // buttons don’t need rich body
|
|
1833
|
+
|
|
1834
|
+
if (rowTextStyle) rowTextStyle.style.display = "flex";
|
|
1835
|
+
|
|
1836
|
+
// show align values (default = empty)
|
|
1837
|
+
if (inspTitleAlign) inspTitleAlign.value = String(it.titleAlign || "");
|
|
1838
|
+
if (inspTextAlign) inspTextAlign.value = String(it.textAlign || "");
|
|
1839
|
+
|
|
1840
|
+
if (useRich){
|
|
1841
|
+
if (fieldTextPlain) fieldTextPlain.style.display = "none";
|
|
1842
|
+
if (fieldTextRich) fieldTextRich.style.display = "block";
|
|
1843
|
+
|
|
1844
|
+
// init editor once (falls back silently if CDN blocked)
|
|
1845
|
+
ensureTinyMCE();
|
|
1846
|
+
|
|
1847
|
+
const html = String(it.textHtml || "").trim()
|
|
1848
|
+
? String(it.textHtml)
|
|
1849
|
+
: plainToHtml(it.text || "");
|
|
1850
|
+
|
|
1851
|
+
setRichHtml(html);
|
|
1852
|
+
} else {
|
|
1853
|
+
if (fieldTextPlain) fieldTextPlain.style.display = "block";
|
|
1854
|
+
if (fieldTextRich) fieldTextRich.style.display = "none";
|
|
1855
|
+
|
|
1856
|
+
inspTextPlain.value = safeText(it.text || "");
|
|
1857
|
+
}
|
|
1688
1858
|
inspCols.value = "1";
|
|
1689
1859
|
|
|
1690
1860
|
// image
|
|
@@ -1722,7 +1892,7 @@
|
|
|
1722
1892
|
if (!s) return;
|
|
1723
1893
|
|
|
1724
1894
|
s.title = inspTitle.value;
|
|
1725
|
-
s.text =
|
|
1895
|
+
s.text = inspTextPlain.value;
|
|
1726
1896
|
s.cols = parseInt(inspCols.value || "1", 10);
|
|
1727
1897
|
s.imageUrl = inspImg.value;
|
|
1728
1898
|
|
|
@@ -1745,7 +1915,25 @@
|
|
|
1745
1915
|
const t = String(it.type || "").toLowerCase();
|
|
1746
1916
|
|
|
1747
1917
|
it.title = inspTitle.value;
|
|
1748
|
-
|
|
1918
|
+
const useRich = (t !== "button");
|
|
1919
|
+
|
|
1920
|
+
it.title = inspTitle.value;
|
|
1921
|
+
|
|
1922
|
+
// save align (empty/default removes the property)
|
|
1923
|
+
const ta = (inspTitleAlign ? String(inspTitleAlign.value || "").trim() : "");
|
|
1924
|
+
const ba = (inspTextAlign ? String(inspTextAlign.value || "").trim() : "");
|
|
1925
|
+
|
|
1926
|
+
if (!ta) delete it.titleAlign; else it.titleAlign = ta;
|
|
1927
|
+
if (!ba) delete it.textAlign; else it.textAlign = ba;
|
|
1928
|
+
|
|
1929
|
+
if (useRich){
|
|
1930
|
+
const html = getRichHtml();
|
|
1931
|
+
it.textHtml = html;
|
|
1932
|
+
it.text = htmlToText(html); // keep a readable plain version for preview/search
|
|
1933
|
+
} else {
|
|
1934
|
+
it.text = inspTextPlain.value;
|
|
1935
|
+
delete it.textHtml;
|
|
1936
|
+
}
|
|
1749
1937
|
|
|
1750
1938
|
if (t === "button"){
|
|
1751
1939
|
it.href = (inspHref.value || "").trim();
|
|
@@ -2345,13 +2533,6 @@
|
|
|
2345
2533
|
}
|
|
2346
2534
|
});
|
|
2347
2535
|
|
|
2348
|
-
// Clear canvas
|
|
2349
|
-
document.getElementById("btn-clear").addEventListener("click", ()=>{
|
|
2350
|
-
state.sections = [];
|
|
2351
|
-
clearSelection();
|
|
2352
|
-
render();
|
|
2353
|
-
});
|
|
2354
|
-
|
|
2355
2536
|
// ----------------------------
|
|
2356
2537
|
// CodeMirror init
|
|
2357
2538
|
// ----------------------------
|