dodanie funkcji
This commit is contained in:
parent
f1a48d4acb
commit
7530c2aa45
10
.idea/.gitignore
vendored
10
.idea/.gitignore
vendored
@ -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
21
.idea/Pathl.Stona.iml
Normal 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>
|
||||||
@ -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>
|
|
||||||
@ -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>
|
||||||
@ -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>
|
||||||
@ -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
107
.idea/workspace.xml
Normal 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>
|
||||||
BIN
__pycache__/app.cpython-313.pyc
Normal file
BIN
__pycache__/app.cpython-313.pyc
Normal file
Binary file not shown.
BIN
apks/__pycache__/mk.cpython-313.pyc
Normal file
BIN
apks/__pycache__/mk.cpython-313.pyc
Normal file
Binary file not shown.
5
apks/mk.py
Normal file
5
apks/mk.py
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
def add(a, b):
|
||||||
|
return a + b
|
||||||
|
|
||||||
|
def sub(a, b):
|
||||||
|
return a - b
|
||||||
188
app.py
188
app.py
@ -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
73
interpreter.py
Normal 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}")
|
||||||
Binary file not shown.
134
static/style.css
134
static/style.css
@ -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
169
templates/editor.html
Normal 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>
|
||||||
@ -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}}">
|
||||||
|
|
||||||
|
<!-- 3 KROPKI -->
|
||||||
|
<div class="menu-btn" onclick="toggleMenu('{{name}}')">⋮</div>
|
||||||
|
|
||||||
|
<!-- 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>
|
<h3>{{name}}</h3>
|
||||||
|
|
||||||
|
<!-- OPIS (Markdown -> HTML) -->
|
||||||
<div class="description">
|
<div class="description">
|
||||||
{{ model.md_content | safe }}
|
{{ model.md_content | safe }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- FORMULARZ -->
|
||||||
<form class="predict-form">
|
<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>
|
||||||
|
|
||||||
|
<!-- OUTPUT -->
|
||||||
<div class="output" id="output-{{name}}"></div>
|
<div class="output" id="output-{{name}}"></div>
|
||||||
|
|
||||||
|
<!-- DOWNLOAD -->
|
||||||
{% if model.meta.downloadable %}
|
{% if model.meta.downloadable %}
|
||||||
<div class="download">
|
<div class="download">
|
||||||
<a href="/download/{{name}}/model.py" target="_blank">Pobierz model.py</a> |
|
<a href="/download/{{name}}/model.py" target="_blank">model.py</a> |
|
||||||
<a href="/download/{{name}}/description.md" target="_blank">Pobierz description.md</a> |
|
<a href="/download/{{name}}/description.md" target="_blank">description.md</a> |
|
||||||
<a href="/download/{{name}}/meta.json" target="_blank">Pobierz meta.json</a>
|
<a href="/download/{{name}}/meta.json" target="_blank">meta.json</a>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<hr>
|
|
||||||
{% endfor %}
|
{% 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>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user