dodanie funkcji

This commit is contained in:
ALicja 2026-01-30 18:08:36 +01:00
parent f1a48d4acb
commit 7530c2aa45
16 changed files with 676 additions and 136 deletions

10
.idea/.gitignore vendored
View File

@ -1,10 +0,0 @@
# Default ignored files
/shelf/
/workspace.xml
# Ignored default folder with query files
/queries/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml
# Editor-based HTTP Client requests
/httpRequests/

21
.idea/Pathl.Stona.iml Normal file
View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="Flask">
<option name="enabled" value="true" />
</component>
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.13 (Pathl.Stona)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
<component name="TemplatesService">
<option name="TEMPLATE_CONFIGURATION" value="Chameleon" />
<option name="TEMPLATE_FOLDERS">
<list>
<option value="$MODULE_DIR$/templates" />
</list>
</option>
</component>
</module>

View File

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.13 (Strona AI)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="Black"> <component name="Black">
<option name="sdkName" value="Python 3.13 (Strona AI)" /> <option name="sdkName" value="Python 3.13 (Pathl.Stona)" />
</component> </component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (Strona AI)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.13 (Pathl.Stona)" project-jdk-type="Python SDK" />
</project> </project>

View File

@ -2,7 +2,7 @@
<project version="4"> <project version="4">
<component name="ProjectModuleManager"> <component name="ProjectModuleManager">
<modules> <modules>
<module fileurl="file://$PROJECT_DIR$/.idea/Strona AI.iml" filepath="$PROJECT_DIR$/.idea/Strona AI.iml" /> <module fileurl="file://$PROJECT_DIR$/.idea/Pathl.Stona.iml" filepath="$PROJECT_DIR$/.idea/Pathl.Stona.iml" />
</modules> </modules>
</component> </component>
</project> </project>

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" /> <mapping directory="" vcs="Git" />
</component> </component>
</project> </project>

107
.idea/workspace.xml Normal file
View File

@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="12dec3ca-9052-46eb-9015-fb58a43ed712" name="Changes" comment="">
<change afterPath="$PROJECT_DIR$/.idea/Pathl.Stona.iml" afterDir="false" />
<change afterPath="$PROJECT_DIR$/apks/mk.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/interpreter.py" afterDir="false" />
<change afterPath="$PROJECT_DIR$/templates/editor.html" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/.gitignore" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/Strona AI.iml" beforeDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/modules.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/modules.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/.idea/vcs.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/vcs.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/app.py" beforeDir="false" afterPath="$PROJECT_DIR$/app.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/static/style.css" beforeDir="false" afterPath="$PROJECT_DIR$/static/style.css" afterDir="false" />
<change beforePath="$PROJECT_DIR$/templates/user.html" beforeDir="false" afterPath="$PROJECT_DIR$/templates/user.html" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="FileTemplateManagerImpl">
<option name="RECENT_TEMPLATES">
<list>
<option value="Python Script" />
</list>
</option>
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 6
}]]></component>
<component name="ProjectId" id="38z9gIsEBluy4uWTRw9cg8JYPk7" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ModuleVcsDetector.initialDetectionPerformed": "true",
"Python.Pathl.Stona.executor": "Run",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
"RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
"SHARE_PROJECT_CONFIGURATION_FILES": "true",
"ai.playground.ignore.import.keys.banner.in.settings": "true",
"git-widget-placeholder": "main",
"nodejs_package_manager_path": "npm",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="RunManager">
<configuration name="Pathl.Stona" type="PythonConfigurationType" factoryName="Python">
<module name="Pathl.Stona" />
<option name="ENV_FILES" value="" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="PARENT_ENVS" value="true" />
<envs>
<env name="PYTHONUNBUFFERED" value="1" />
</envs>
<option name="SDK_HOME" value="" />
<option name="WORKING_DIRECTORY" value="" />
<option name="IS_MODULE_SDK" value="false" />
<option name="ADD_CONTENT_ROOTS" value="true" />
<option name="ADD_SOURCE_ROOTS" value="true" />
<EXTENSION ID="PythonCoverageRunConfigurationExtension" runner="coverage.py" />
<option name="RUN_TOOL" value="" />
<option name="SCRIPT_NAME" value="$PROJECT_DIR$/app.py" />
<option name="PARAMETERS" value="" />
<option name="SHOW_COMMAND_LINE" value="false" />
<option name="EMULATE_TERMINAL" value="false" />
<option name="MODULE_MODE" value="false" />
<option name="REDIRECT_INPUT" value="false" />
<option name="INPUT_FILE" value="" />
<method v="2" />
</configuration>
</component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-js-predefined-d6986cc7102b-9b0f141eb926-JavaScript-PY-253.29346.142" />
<option value="bundled-python-sdk-f2b7a9f6281b-6e1f45a539f7-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-253.29346.142" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="12dec3ca-9052-46eb-9015-fb58a43ed712" name="Changes" comment="" />
<created>1769791641577</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1769791641577</updated>
<workItem from="1769791642745" duration="1260000" />
</task>
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
<option name="version" value="3" />
</component>
<component name="com.intellij.coverage.CoverageDataManagerImpl">
<SUITE FILE_PATH="coverage/Pathl_Stona$Pathl_Stona.coverage" NAME="Pathl.Stona Coverage Results" MODIFIED="1769792843549" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
</component>
</project>

Binary file not shown.

Binary file not shown.

5
apks/mk.py Normal file
View File

@ -0,0 +1,5 @@
def add(a, b):
return a + b
def sub(a, b):
return a - b

188
app.py
View File

@ -1,17 +1,27 @@
from flask import Flask, request, render_template, jsonify, send_from_directory from flask import Flask, request, render_template, jsonify, send_file
import os, json, importlib.util import os, json, importlib, importlib.util, sys, re
from werkzeug.utils import secure_filename from markdown import markdown
import markdown
app = Flask(__name__)
UPLOAD_FOLDER = "models"
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
ADMIN_KEY = "supersecretadminkey"
app = Flask(__name__)
# FOLDERS
UPLOAD_FOLDER = "scripts"
MODELS_FOLDER = "models"
APKS_FOLDER = "apks"
os.makedirs(UPLOAD_FOLDER, exist_ok=True)
os.makedirs(MODELS_FOLDER, exist_ok=True)
sys.path.insert(0, os.path.join(os.getcwd(), APKS_FOLDER))
# GLOBAL MODELS
MODELS = {} MODELS = {}
# ---- Load models at startup ----
def load_models(): def load_models():
for folder in os.listdir(UPLOAD_FOLDER): global MODELS
path = os.path.join(UPLOAD_FOLDER, folder) MODELS = {}
for folder in os.listdir(MODELS_FOLDER):
path = os.path.join(MODELS_FOLDER, folder)
if not os.path.isdir(path): if not os.path.isdir(path):
continue continue
meta_path = os.path.join(path, "meta.json") meta_path = os.path.join(path, "meta.json")
@ -38,7 +48,8 @@ def load_models():
load_models() load_models()
# ---- DASHBOARD ----
# ---------------- DASHBOARD ----------------
@app.route("/") @app.route("/")
def dashboard(): def dashboard():
models_with_md = {} models_with_md = {}
@ -47,16 +58,81 @@ def dashboard():
try: try:
md_path = os.path.join(model["path"], model["meta"]["description_file"]) md_path = os.path.join(model["path"], model["meta"]["description_file"])
with open(md_path, "r", encoding="utf-8") as f: with open(md_path, "r", encoding="utf-8") as f:
md_text = f.read() md_content = markdown(f.read())
# konwersja Markdown -> HTML except:
md_content = markdown.markdown(md_text)
except Exception:
md_content = "<p>Brak opisu.</p>" md_content = "<p>Brak opisu.</p>"
models_with_md[name] = {**model, "md_content": md_content} models_with_md[name] = {**model, "md_content": md_content}
return render_template("user.html", models=models_with_md) return render_template("user.html", models=models_with_md)
# ---- PREDICT ----
# ---------------- EDITOR ----------------
@app.route("/editor/<model_name>")
def editor(model_name):
return render_template("editor.html", model_name=model_name)
# ---------------- RUN SCRIPT (MKScript) ----------------
@app.route("/run_script", methods=["POST"])
def run_script():
code = request.json.get("code", "")
output = []
variables = {}
modules = {}
try:
lines = code.split(";")
for line in lines:
line = line.strip()
if not line:
continue
# use mk
if line.startswith("use "):
name = line.split()[1]
mod = importlib.import_module(name)
modules[name] = mod
variables[name] = mod
continue
# print(...)
if line.startswith("print"):
inside = line[line.find("(")+1:line.rfind(")")]
output.append(str(eval_expr(inside, variables)))
continue
# typy int/float/bool
if any(line.startswith(t) for t in ["int ", "float ", "bool "]):
_, rest = line.split(" ", 1)
var, expr = rest.split("=")
variables[var.strip()] = eval_expr(expr.strip(), variables)
continue
# wywołanie funkcji np mk.add(1,2)
if "." in line and "(" in line:
obj, rest = line.split(".", 1)
fname = rest[:rest.find("(")]
args = rest[rest.find("(")+1:rest.find(")")]
args = [eval_expr(a.strip(), variables) for a in args.split(",")]
result = getattr(modules[obj], fname)(*args)
variables["_"] = result
continue
except Exception as e:
return jsonify({"error": str(e)})
return jsonify({
"output": output,
"variables": {k: str(v) for k, v in variables.items()}
})
def eval_expr(expr, variables):
expr = expr.replace("true", "True").replace("false", "False")
return eval(expr, {}, variables)
# ---------------- PREDICT ----------------
@app.route("/predict/<model_name>", methods=["POST"]) @app.route("/predict/<model_name>", methods=["POST"])
def predict(model_name): def predict(model_name):
model_entry = MODELS.get(model_name) model_entry = MODELS.get(model_name)
@ -65,9 +141,7 @@ def predict(model_name):
func = model_entry["func"] func = model_entry["func"]
inputs = model_entry["inputs"] inputs = model_entry["inputs"]
kwargs = {} kwargs = {inp: request.form.get(inp) for inp in inputs}
for inp in inputs:
kwargs[inp] = request.form.get(inp)
try: try:
for k in kwargs: for k in kwargs:
@ -81,64 +155,28 @@ def predict(model_name):
return jsonify({"output": str(output)}) return jsonify({"output": str(output)})
# ---- ADD MODEL ----
@app.route("/add_model", methods=["POST"])
def add_model():
admin_key = request.form.get("admin_key")
if admin_key != ADMIN_KEY:
return "Brak dostępu", 403
name = request.form["name"] # ---------------- DOWNLOAD SCRIPT ----------------
function_name = request.form["function_name"] @app.route("/download_script/<filename>")
inputs = request.form.getlist("inputs") def download_script(filename):
model_py = request.files["model_py"] path = os.path.join(UPLOAD_FOLDER, filename)
description_file = request.files["description"] if os.path.exists(path):
return send_file(path, as_attachment=True)
return "Plik nie istnieje", 404
folder_name = secure_filename(name)
folder_path = os.path.join(UPLOAD_FOLDER, folder_name)
os.makedirs(folder_path, exist_ok=True)
# zapis plików # ---------------- UPLOAD SCRIPT ----------------
model_py.save(os.path.join(folder_path, "model.py")) @app.route("/upload_script", methods=["POST"])
description_file.save(os.path.join(folder_path, "description.md")) def upload_script():
file = request.files.get("file")
if not file:
return "Nie przesłano pliku", 400
filename = file.filename
path = os.path.join(UPLOAD_FOLDER, filename)
file.save(path)
return "OK"
# meta.json
meta = {
"name": name,
"function_name": function_name,
"inputs": inputs,
"description_file": "description.md",
"downloadable": True, # można pobrać pliki
"type": request.form.get("type", "algorithmic")
}
with open(os.path.join(folder_path, "meta.json"), "w") as f:
json.dump(meta, f)
# załaduj od razu
spec = importlib.util.spec_from_file_location("model_module", os.path.join(folder_path, "model.py"))
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
func = getattr(module, function_name)
MODELS[folder_name] = {
"func": func,
"inputs": inputs,
"meta": meta,
"path": folder_path
}
return "Dodano model!"
# ---- DOWNLOAD FILE ----
@app.route("/download/<model_name>/<file_name>")
def download_file(model_name, file_name):
model_entry = MODELS.get(model_name)
if not model_entry:
return "Model nie istnieje", 404
folder_path = model_entry["path"]
if file_name not in ["model.py", "description.md", "meta.json"]:
return "Nieprawidłowy plik", 400
return send_from_directory(folder_path, file_name, as_attachment=True)
# ---------------- MAIN ----------------
if __name__ == "__main__": if __name__ == "__main__":
app.run(debug=True) app.run(port=4500)

73
interpreter.py Normal file
View File

@ -0,0 +1,73 @@
import re, importlib
variables = {}
modules = {}
functions = {}
# Rejestracja “handlerów” dla linii
handlers = []
def eval_expr(expr):
"""Bezpieczne obliczanie wyrażeń z użyciem zmiennych"""
expr = expr.replace("true", "True").replace("false", "False")
return eval(expr, {}, variables)
def handle_use(line):
if line.startswith("use "):
name = line.split()[1]
modules[name] = importlib.import_module(f"apks.{name}")
variables[name] = modules[name]
return True
return False
def handle_print(line):
if line.startswith("print"):
inside = re.search(r"\((.*)\)", line).group(1)
print(eval_expr(inside))
return True
return False
def handle_var(line):
for t in ["int ", "float ", "bool ", "string "]:
if line.startswith(t):
_, rest = line.split(" ", 1)
var, expr = rest.split("=")
val = expr.strip()
if t.strip() == "string":
val = val.strip('"').strip("'")
else:
val = eval_expr(val)
variables[var.strip()] = val
return True
return False
def handle_func_call(line):
if "." in line and "(" in line:
obj, rest = line.split(".", 1)
fname = rest[:rest.find("(")]
args_str = rest[rest.find("(")+1:rest.rfind(")")]
args = [eval_expr(a.strip()) for a in args_str.split(",") if a.strip()]
result = getattr(modules[obj], fname)(*args)
variables["_"] = result # ostatni wynik
return True
return False
# Dodajemy wszystkie handlery do listy
handlers.extend([handle_use, handle_print, handle_var, handle_func_call])
def run(code):
"""Interpreter MKScript"""
lines = code.split(";")
for line in lines:
line = line.strip()
if not line or line.startswith("//"): # komentarze
continue
# uruchamiamy wszystkie handlery aż któryś obsłuży linię
handled = False
for h in handlers:
if h(line):
handled = True
break
if not handled:
print(f"Nieznana linia: {line}")

View File

@ -1,4 +1,6 @@
/* Reset podstawowy */ /* =========================
RESET PODSTAWOWY
========================= */
* { * {
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -6,14 +8,18 @@
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
} }
/* Body */ /* =========================
BODY
========================= */
body { body {
background: #f4f7fc; background: #f4f7fc;
color: #333; color: #333;
padding: 20px; padding: 20px;
} }
/* Nagłówek */ /* =========================
NAGŁÓWEK
========================= */
h2 { h2 {
text-align: center; text-align: center;
margin-bottom: 30px; margin-bottom: 30px;
@ -21,20 +27,26 @@ h2 {
font-size: 2em; font-size: 2em;
} }
/* Kontener modeli */ /* =========================
KONTENER MODELI
========================= */
#models-container { #models-container {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 20px; gap: 20px;
} }
/* Karta modelu */ /* =========================
KARTA MODELU
========================= */
.model-card { .model-card {
position: relative;
background: #ffffff; background: #ffffff;
border-radius: 15px; border-radius: 15px;
padding: 20px; padding: 20px;
box-shadow: 0 6px 18px rgba(0,0,0,0.08); box-shadow: 0 6px 18px rgba(0,0,0,0.08);
transition: transform 0.2s, box-shadow 0.2s; transition: transform 0.2s, box-shadow 0.2s;
overflow: hidden;
} }
.model-card:hover { .model-card:hover {
@ -42,7 +54,76 @@ h2 {
box-shadow: 0 12px 24px rgba(0,0,0,0.12); box-shadow: 0 12px 24px rgba(0,0,0,0.12);
} }
/* Nazwa modelu */ /* =========================
3 KROPKI (MENU)
========================= */
.menu-btn {
position: absolute;
top: 12px;
right: 14px;
cursor: pointer;
font-size: 22px;
color: #777;
user-select: none;
}
.menu-btn:hover {
color: #000;
}
/* =========================
BOCZNY PANEL (SLIDE)
========================= */
.side-menu {
position: absolute;
top: 0;
right: -260px;
width: 250px;
height: 100%;
background: #111;
color: #fff;
padding: 20px;
transition: right 0.3s ease;
z-index: 20;
display: flex;
flex-direction: column;
gap: 10px;
}
.side-menu.open {
right: 0;
}
.side-menu h4 {
margin-bottom: 10px;
border-bottom: 1px solid #333;
padding-bottom: 5px;
}
.side-menu input {
padding: 8px;
border-radius: 6px;
border: none;
font-size: 0.9em;
}
.side-menu button {
padding: 8px;
border-radius: 8px;
border: none;
cursor: pointer;
font-weight: bold;
background: #6c63ff;
color: white;
}
.side-menu button:hover {
background: #574fd6;
}
/* =========================
NAZWA MODELU
========================= */
.model-card h3 { .model-card h3 {
margin-bottom: 15px; margin-bottom: 15px;
color: #1a1a1a; color: #1a1a1a;
@ -51,7 +132,9 @@ h2 {
padding-bottom: 5px; padding-bottom: 5px;
} }
/* Opis modelu (.md) */ /* =========================
OPIS MODELU (Markdown)
========================= */
.description { .description {
font-style: italic; font-style: italic;
color: #555; color: #555;
@ -59,7 +142,9 @@ h2 {
line-height: 1.5; line-height: 1.5;
} }
/* Formularz */ /* =========================
FORMULARZ
========================= */
form.predict-form { form.predict-form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
@ -84,7 +169,9 @@ form.predict-form input:focus {
outline: none; outline: none;
} }
/* Przycisk */ /* =========================
PRZYCISK
========================= */
form.predict-form button { form.predict-form button {
padding: 10px 15px; padding: 10px 15px;
background: #6c63ff; background: #6c63ff;
@ -101,14 +188,18 @@ form.predict-form button:hover {
transform: translateY(-2px); transform: translateY(-2px);
} }
/* Output */ /* =========================
OUTPUT
========================= */
.output { .output {
margin-top: 10px; margin-top: 10px;
font-weight: bold; font-weight: bold;
color: #1a73e8; color: #1a73e8;
} }
/* Download linki */ /* =========================
DOWNLOAD
========================= */
.download { .download {
margin-top: 15px; margin-top: 15px;
font-size: 0.9em; font-size: 0.9em;
@ -126,7 +217,9 @@ form.predict-form button:hover {
text-decoration: underline; text-decoration: underline;
} }
/* Linie oddzielające karty */ /* =========================
LINIE
========================= */
hr { hr {
border: none; border: none;
height: 1px; height: 1px;
@ -134,7 +227,9 @@ hr {
margin: 20px 0; margin: 20px 0;
} }
/* Responsywność */ /* =========================
RESPONSYWNOŚĆ
========================= */
@media (max-width: 600px) { @media (max-width: 600px) {
h2 { h2 {
font-size: 1.5em; font-size: 1.5em;
@ -144,7 +239,18 @@ hr {
padding: 15px; padding: 15px;
} }
form.predict-form input, form.predict-form button { form.predict-form input,
form.predict-form button {
font-size: 0.9em; font-size: 0.9em;
} }
} }
.side-menu {
pointer-events: none;
}
.side-menu.open {
pointer-events: auto;
}
.side-menu {
position: fixed;
}

169
templates/editor.html Normal file
View File

@ -0,0 +1,169 @@
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>MKScript Editor {{model_name}}</title>
<!-- CodeMirror CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/codemirror.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/theme/dracula.min.css">
<style>
body {
background: #282a36;
color: #f8f8f2;
font-family: 'Fira Code', monospace;
margin: 0;
padding: 20px;
}
h2 {
text-align: center;
color: #50fa7b;
margin-bottom: 15px;
}
button {
margin: 5px 5px 5px 0;
padding: 8px 16px;
border: none;
border-radius: 6px;
cursor: pointer;
background: #6272a4;
color: #f8f8f2;
font-weight: bold;
transition: 0.2s;
}
button:hover {
background: #50fa7b;
color: #282a36;
}
#editor-container {
margin-top: 10px;
border-radius: 8px;
overflow: hidden;
border: 1px solid #6272a4;
}
.CodeMirror {
height: 400px;
font-size: 14px;
line-height: 1.5;
}
.panel {
margin-top: 20px;
background: #44475a;
padding: 10px;
border-radius: 6px;
overflow-x: auto;
}
.panel h3 {
margin: 0 0 5px 0;
font-size: 14px;
color: #ff79c6;
}
#output, #debug {
white-space: pre-wrap;
color: #f1fa8c;
}
</style>
</head>
<body>
<h2>MKScript Editor {{model_name}}</h2>
<div>
<button onclick="run()">▶ Uruchom</button>
<button onclick="check()">✓ Sprawdź</button>
<button onclick="downloadFile()">💾 Pobierz</button>
<!-- Wczytywanie pliku -->
<input type="file" id="upload" accept=".ql" onchange="uploadFile()">
<a href="/" style="color:#50fa7b; margin-left: 10px;">← Wróć</a>
</div>
<div id="editor-container">
<textarea id="code">
use mk;
int a = 5;
int b = 10;
int c = mk.add(a, b);
print(c);
</textarea>
</div>
<div class="panel">
<h3>Console Output</h3>
<pre id="output"></pre>
</div>
<div class="panel">
<h3>Debugger (zmienne)</h3>
<pre id="debug"></pre>
</div>
<!-- CodeMirror JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/codemirror.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.65.9/mode/python/python.min.js"></script>
<script>
const editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
mode: "python",
theme: "dracula",
matchBrackets: true,
indentUnit: 4,
tabSize: 4,
autofocus: true
});
async function run() {
const code = editor.getValue();
const res = await fetch("/run_script", {
method: "POST",
headers: {"Content-Type":"application/json"},
body: JSON.stringify({code})
});
const data = await res.json();
if(data.error){
document.getElementById("output").textContent = "ERROR: "+data.error;
document.getElementById("debug").textContent = "";
}else{
document.getElementById("output").textContent = data.output.join("\n");
document.getElementById("debug").textContent = JSON.stringify(data.variables,null,2);
}
}
function check(){
const code = editor.getValue();
if(!code.includes(";")) alert("Brak średników ;");
else alert("Składnia OK ✔");
}
function downloadFile(){
const blob = new Blob([editor.getValue()], {type:"text/plain"});
const a = document.createElement("a");
a.href = URL.createObjectURL(blob);
a.download = "{{model_name}}.ql";
a.click();
}
function uploadFile(){
const file = document.getElementById("upload").files[0];
if(!file) return;
const form = new FormData();
form.append("file", file);
fetch("/upload_script", {method:"POST", body:form})
.then(r => r.text())
.then(alert);
}
</script>
</body>
</html>

View File

@ -1,42 +1,71 @@
<!DOCTYPE html> <!DOCTYPE html>
<html> <html lang="pl">
<head> <head>
<meta charset="UTF-8">
<title>AI Models</title> <title>AI Models</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head> </head>
<body> <body>
<h2>Lista modeli AI</h2> <h2>Lista modeli AI</h2>
<div id="models-container"> <div id="models-container">
{% for name, model in models.items() %} {% for name, model in models.items() %}
<div class="model-card" id="model-{{name}}"> <div class="model-card" id="model-{{name}}">
<h3>{{name}}</h3>
<div class="description">
{{ model.md_content | safe }}
</div>
<!-- 3 KROPKI -->
<div class="menu-btn" onclick="toggleMenu('{{name}}')"></div>
<form class="predict-form"> <!-- WYSUWANY PANEL -->
<div class="side-menu" id="menu-{{name}}">
<h4>Akcje modelu</h4>
<input type="text" value="{{name}}" readonly>
<button onclick="goToEditor('{{name}}')">
Edytor kodu
</button>
<button onclick="toggleMenu('{{name}}')">
Zamknij
</button>
</div>
<!-- NAZWA -->
<h3>{{name}}</h3>
<!-- OPIS (Markdown -> HTML) -->
<div class="description">
{{ model.md_content | safe }}
</div>
<!-- FORMULARZ -->
<form class="predict-form">
{% for inp in model.inputs %} {% for inp in model.inputs %}
<label>{{inp}}:</label> <label>{{inp}}:</label>
<input name="{{inp}}"><br> <input name="{{inp}}">
{% endfor %} {% endfor %}
<button type="submit">Wyślij</button> <button type="submit">Wyślij</button>
</form> </form>
<div class="output" id="output-{{name}}"></div>
{% if model.meta.downloadable %} <!-- OUTPUT -->
<div class="download"> <div class="output" id="output-{{name}}"></div>
<a href="/download/{{name}}/model.py" target="_blank">Pobierz model.py</a> |
<a href="/download/{{name}}/description.md" target="_blank">Pobierz description.md</a> | <!-- DOWNLOAD -->
<a href="/download/{{name}}/meta.json" target="_blank">Pobierz meta.json</a> {% if model.meta.downloadable %}
</div> <div class="download">
{% endif %} <a href="/download/{{name}}/model.py" target="_blank">model.py</a> |
<a href="/download/{{name}}/description.md" target="_blank">description.md</a> |
<a href="/download/{{name}}/meta.json" target="_blank">meta.json</a>
</div> </div>
<hr> {% endif %}
{% endfor %}
</div>
{% endfor %}
</div> </div>
<script> <script>
/* ---- PREDICT ---- */
document.querySelectorAll(".predict-form").forEach(form => { document.querySelectorAll(".predict-form").forEach(form => {
form.addEventListener("submit", async (e) => { form.addEventListener("submit", async (e) => {
e.preventDefault(); e.preventDefault();
@ -54,6 +83,18 @@ document.querySelectorAll(".predict-form").forEach(form => {
outputDiv.textContent = data.output; outputDiv.textContent = data.output;
}); });
}); });
/* ---- MENU ---- */
function toggleMenu(name) {
const menu = document.getElementById("menu-" + name);
menu.classList.toggle("open");
}
/* ---- EDITOR ---- */
function goToEditor(name) {
window.location.href = `/editor/${name}`;
}
</script> </script>
</body> </body>
</html> </html>