- update документация - промт для ии полу конфигурируемый
This commit is contained in:
+175
-15
@@ -14,13 +14,20 @@ from .diagram_schemes import (
|
||||
DiagramScheme,
|
||||
SCHEME_ID_RE,
|
||||
ensure_user_schemes,
|
||||
get_schemes,
|
||||
load_schemes_from_path,
|
||||
reload_schemes,
|
||||
save_schemes,
|
||||
user_schemes_path,
|
||||
)
|
||||
from .dnd import enable_file_drop, first_markdown
|
||||
from .help_content import SCHEMES_HELP, USAGE_HELP, load_prompt_catalog
|
||||
from .help_content import (
|
||||
SCHEMES_HELP,
|
||||
USAGE_HELP,
|
||||
compose_prompt,
|
||||
load_docs_catalog,
|
||||
load_prompt_catalog,
|
||||
)
|
||||
from .pipeline import ConvertRequest, convert, default_output_path, timestamped_output_path
|
||||
from .profiles import (
|
||||
DEFAULT_HEADING_NUMBERING,
|
||||
@@ -244,6 +251,7 @@ class Md2GostApp:
|
||||
self.template_var = tk.StringVar()
|
||||
self.title_var = tk.StringVar()
|
||||
self.assignment_var = tk.StringVar()
|
||||
self.styles_var = tk.StringVar()
|
||||
self.fallback_var = tk.StringVar()
|
||||
self.format_var = tk.StringVar()
|
||||
self.plantuml_var = tk.StringVar()
|
||||
@@ -266,6 +274,7 @@ class Md2GostApp:
|
||||
|
||||
help_menu = tk.Menu(menubar, tearoff=0)
|
||||
help_menu.add_command(label="Инструкция", command=lambda: self._open_help_window("usage"))
|
||||
help_menu.add_command(label="Документация", command=lambda: self._open_help_window("docs"))
|
||||
help_menu.add_command(label="Схемы", command=lambda: self._open_help_window("schemes"))
|
||||
help_menu.add_command(label="Промпт для ИИ", command=lambda: self._open_help_window("prompts"))
|
||||
menubar.add_cascade(label="Справка", menu=help_menu)
|
||||
@@ -313,8 +322,8 @@ class Md2GostApp:
|
||||
win = tk.Toplevel(self.root)
|
||||
win.title("Настройки — Файлы")
|
||||
win.configure(bg=PAPER)
|
||||
win.minsize(520, 200)
|
||||
win.geometry("620x240")
|
||||
win.minsize(520, 240)
|
||||
win.geometry("620x280")
|
||||
win.transient(self.root)
|
||||
frame = ttk.Frame(win, padding=12)
|
||||
frame.pack(fill=tk.BOTH, expand=True)
|
||||
@@ -325,13 +334,16 @@ class Md2GostApp:
|
||||
tip="DOCX титула, вставляется перед телом отчёта")
|
||||
self._row_path(frame, 2, "Бланк задания", self.assignment_var, self._browse_assignment, clearable=True,
|
||||
tip="DOCX бланка задания, вставляется перед телом отчёта")
|
||||
self._row_path(frame, 3, "Стили JSON", self.styles_var, self._browse_styles, clearable=True,
|
||||
tip="Оверлей md2gost.styles.json поверх пресета типа документа")
|
||||
hint = ttk.Label(
|
||||
frame,
|
||||
text="Пустой шаблон — встроенный Template.docx. Титул и задание вставляются перед телом отчёта.",
|
||||
text="Пустой шаблон — встроенный Template.docx. Титул и задание вставляются перед телом отчёта.\n"
|
||||
"Стили JSON — опционально; также подхватывается md2gost.styles.json рядом с .md.",
|
||||
style="Hint.TLabel",
|
||||
wraplength=560,
|
||||
)
|
||||
hint.grid(row=3, column=0, columnspan=2, sticky="w", pady=(8, 0))
|
||||
hint.grid(row=4, column=0, columnspan=2, sticky="w", pady=(8, 0))
|
||||
win.protocol("WM_DELETE_WINDOW", lambda: self._close_toplevel(win, "_win_files"))
|
||||
return win
|
||||
|
||||
@@ -456,6 +468,7 @@ class Md2GostApp:
|
||||
def _create_help_window(self, kind: str) -> tk.Toplevel:
|
||||
titles = {
|
||||
"usage": "Справка — Инструкция",
|
||||
"docs": "Справка — Документация",
|
||||
"schemes": "Справка — Схемы",
|
||||
"prompts": "Справка — Промпт для ИИ",
|
||||
}
|
||||
@@ -463,12 +476,17 @@ class Md2GostApp:
|
||||
win.title(titles.get(kind, "Справка"))
|
||||
win.configure(bg=PAPER)
|
||||
win.minsize(560, 400)
|
||||
win.geometry("700x560")
|
||||
if kind == "docs":
|
||||
win.geometry("780x560")
|
||||
else:
|
||||
win.geometry("700x560")
|
||||
win.transient(self.root)
|
||||
frame = ttk.Frame(win, padding=12)
|
||||
frame.pack(fill=tk.BOTH, expand=True)
|
||||
if kind == "usage":
|
||||
self._build_help(frame)
|
||||
elif kind == "docs":
|
||||
self._build_docs_browser(frame)
|
||||
elif kind == "schemes":
|
||||
self._build_schemes_help(frame)
|
||||
else:
|
||||
@@ -540,6 +558,60 @@ class Md2GostApp:
|
||||
box = self._readonly_text(parent, USAGE_HELP.strip() + "\n")
|
||||
box.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
def _build_docs_browser(self, parent) -> None:
|
||||
ttk.Label(
|
||||
parent,
|
||||
text="Страницы слева; ссылки вида (file.md) в тексте открывайте тем же списком. "
|
||||
"Markdown показывается как есть.",
|
||||
style="Hint.TLabel",
|
||||
wraplength=740,
|
||||
).pack(anchor=tk.W, pady=(0, 8))
|
||||
|
||||
catalog = load_docs_catalog()
|
||||
if not catalog:
|
||||
ttk.Label(
|
||||
parent,
|
||||
text="Документация не найдена (папка docs/ рядом с программой или внутри сборки).",
|
||||
style="Hint.TLabel",
|
||||
).pack(anchor=tk.W)
|
||||
return
|
||||
|
||||
self._docs_catalog = catalog
|
||||
body = ttk.Frame(parent)
|
||||
body.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
left = ttk.Frame(body)
|
||||
left.pack(side=tk.LEFT, fill=tk.Y, padx=(0, 10))
|
||||
self._docs_list = tk.Listbox(left, height=22, width=28, exportselection=False)
|
||||
self._docs_list.pack(side=tk.TOP, fill=tk.BOTH, expand=True)
|
||||
for _name, title, _text in catalog:
|
||||
self._docs_list.insert(tk.END, title)
|
||||
self._docs_list.bind("<<ListboxSelect>>", self._on_docs_select)
|
||||
_tip(self._docs_list, "Разделы документации (docs/*.md)")
|
||||
|
||||
right = ttk.Frame(body)
|
||||
right.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
self._docs_view = self._readonly_text(right, catalog[0][2])
|
||||
self._docs_view.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
self._docs_list.selection_set(0)
|
||||
self._docs_list.see(0)
|
||||
|
||||
def _on_docs_select(self, _event=None) -> None:
|
||||
if not hasattr(self, "_docs_list") or not hasattr(self, "_docs_catalog"):
|
||||
return
|
||||
sel = self._docs_list.curselection()
|
||||
if not sel:
|
||||
return
|
||||
idx = int(sel[0])
|
||||
if idx < 0 or idx >= len(self._docs_catalog):
|
||||
return
|
||||
text = self._docs_catalog[idx][2]
|
||||
self._docs_view.configure(state=tk.NORMAL)
|
||||
self._docs_view.delete("1.0", tk.END)
|
||||
self._docs_view.insert("1.0", text)
|
||||
self._docs_view.configure(state=tk.DISABLED)
|
||||
|
||||
def _build_schemes_help(self, parent) -> None:
|
||||
ttk.Label(
|
||||
parent,
|
||||
@@ -553,7 +625,9 @@ class Md2GostApp:
|
||||
catalog = load_prompt_catalog()
|
||||
ttk.Label(
|
||||
parent,
|
||||
text="Скопируйте промпт целиком и вставьте в ChatGPT / Cursor / Copilot. Следующим сообщением — тип, тема, черновик.",
|
||||
text="Скопируйте промпт и вставьте в ChatGPT / Cursor / Copilot. "
|
||||
"Кнопки схем дописывают макросы из md2gost.schemes.json — "
|
||||
"когда ИИ должен знать C4/BPMN/свои шаблоны. Следующим сообщением — тип, тема, черновик.",
|
||||
style="Hint.TLabel",
|
||||
wraplength=640,
|
||||
).pack(anchor=tk.W, pady=(0, 8))
|
||||
@@ -575,32 +649,113 @@ class Md2GostApp:
|
||||
ttk.Label(row, text="Промпт").pack(side=tk.LEFT, padx=(0, 8))
|
||||
box = ttk.Combobox(row, textvariable=self._prompt_choice, values=titles, state="readonly")
|
||||
box.pack(side=tk.LEFT, fill=tk.X, expand=True)
|
||||
box.bind("<<ComboboxSelected>>", lambda _e: self._show_selected_prompt())
|
||||
box.bind("<<ComboboxSelected>>", lambda _e: self._refresh_composed_prompt())
|
||||
btn = ttk.Button(row, text="Скопировать", command=self._copy_selected_prompt)
|
||||
btn.pack(side=tk.LEFT, padx=(8, 0))
|
||||
_tip(btn, "Скопировать выбранный промпт в буфер обмена")
|
||||
_tip(btn, "Скопировать собранный промпт (база + выбранные схемы) в буфер")
|
||||
|
||||
self._prompt_view = self._readonly_text(parent, self._prompt_texts[titles[0]])
|
||||
schemes_frame = ttk.LabelFrame(parent, text="Дописать схемы в промпт", padding=6)
|
||||
schemes_frame.pack(fill=tk.X, pady=(0, 6))
|
||||
ttk.Label(
|
||||
schemes_frame,
|
||||
text="UML/Mermaid и так конвертируются в Рисунок; схемы нужны для макросов.",
|
||||
style="Hint.TLabel",
|
||||
wraplength=620,
|
||||
).pack(anchor=tk.W, pady=(0, 4))
|
||||
|
||||
try:
|
||||
ensure_user_schemes()
|
||||
schemes_map = get_schemes()
|
||||
except Exception:
|
||||
schemes_map = {}
|
||||
self._prompt_scheme_ids = sorted(schemes_map.keys())
|
||||
self._prompt_scheme_vars: dict[str, tk.BooleanVar] = {}
|
||||
self._prompt_schemes_by_id = dict(schemes_map)
|
||||
|
||||
toggles = ttk.Frame(schemes_frame)
|
||||
toggles.pack(fill=tk.X)
|
||||
if not self._prompt_scheme_ids:
|
||||
ttk.Label(
|
||||
toggles,
|
||||
text="Схемы не найдены (md2gost.schemes.json).",
|
||||
style="Hint.TLabel",
|
||||
).pack(anchor=tk.W)
|
||||
else:
|
||||
for sid in self._prompt_scheme_ids:
|
||||
var = tk.BooleanVar(value=False)
|
||||
self._prompt_scheme_vars[sid] = var
|
||||
title = schemes_map[sid].title or sid
|
||||
cb = ttk.Checkbutton(
|
||||
toggles,
|
||||
text=f"{title} ({sid})",
|
||||
variable=var,
|
||||
command=self._refresh_composed_prompt,
|
||||
)
|
||||
cb.pack(anchor=tk.W)
|
||||
_tip(cb, f"Добавить в промпт макросы схемы ```uml-{sid}")
|
||||
|
||||
ctrl = ttk.Frame(schemes_frame)
|
||||
ctrl.pack(fill=tk.X, pady=(4, 0))
|
||||
btn_all = ttk.Button(ctrl, text="Все", command=self._prompt_schemes_select_all)
|
||||
btn_all.pack(side=tk.LEFT, padx=(0, 6))
|
||||
_tip(btn_all, "Включить все схемы")
|
||||
btn_none = ttk.Button(ctrl, text="Сбросить", command=self._prompt_schemes_clear)
|
||||
btn_none.pack(side=tk.LEFT)
|
||||
_tip(btn_none, "Выключить все схемы")
|
||||
|
||||
self._prompt_view = self._readonly_text(parent, "")
|
||||
self._prompt_view.pack(fill=tk.BOTH, expand=True)
|
||||
self._copy_status = ttk.Label(parent, text="", style="Hint.TLabel")
|
||||
self._copy_status.pack(anchor=tk.W, pady=(6, 0))
|
||||
self._refresh_composed_prompt()
|
||||
|
||||
def _show_selected_prompt(self) -> None:
|
||||
text = self._prompt_texts.get(self._prompt_choice.get(), "")
|
||||
def _selected_prompt_schemes(self) -> list:
|
||||
out = []
|
||||
for sid in getattr(self, "_prompt_scheme_ids", []):
|
||||
var = self._prompt_scheme_vars.get(sid)
|
||||
if var is not None and var.get():
|
||||
scheme = self._prompt_schemes_by_id.get(sid)
|
||||
if scheme is not None:
|
||||
out.append(scheme)
|
||||
return out
|
||||
|
||||
def _composed_prompt_text(self) -> str:
|
||||
base = self._prompt_texts.get(self._prompt_choice.get(), "")
|
||||
return compose_prompt(base, self._selected_prompt_schemes())
|
||||
|
||||
def _refresh_composed_prompt(self) -> None:
|
||||
if not hasattr(self, "_prompt_view"):
|
||||
return
|
||||
text = self._composed_prompt_text()
|
||||
self._prompt_view.configure(state=tk.NORMAL)
|
||||
self._prompt_view.delete("1.0", tk.END)
|
||||
self._prompt_view.insert("1.0", text)
|
||||
self._prompt_view.configure(state=tk.DISABLED)
|
||||
self._copy_status.configure(text="")
|
||||
if hasattr(self, "_copy_status"):
|
||||
self._copy_status.configure(text="")
|
||||
|
||||
def _prompt_schemes_select_all(self) -> None:
|
||||
for var in self._prompt_scheme_vars.values():
|
||||
var.set(True)
|
||||
self._refresh_composed_prompt()
|
||||
|
||||
def _prompt_schemes_clear(self) -> None:
|
||||
for var in self._prompt_scheme_vars.values():
|
||||
var.set(False)
|
||||
self._refresh_composed_prompt()
|
||||
|
||||
def _copy_selected_prompt(self) -> None:
|
||||
text = self._prompt_texts.get(self._prompt_choice.get(), "")
|
||||
text = self._composed_prompt_text()
|
||||
if not text:
|
||||
return
|
||||
self.root.clipboard_clear()
|
||||
self.root.clipboard_append(text)
|
||||
self.root.update_idletasks()
|
||||
self._copy_status.configure(text="Скопировано в буфер обмена — можно вставлять в ИИ.")
|
||||
n = len(self._selected_prompt_schemes())
|
||||
extra = f" (+{n} схем)" if n else ""
|
||||
self._copy_status.configure(
|
||||
text=f"Скопировано в буфер обмена{extra} — можно вставлять в ИИ."
|
||||
)
|
||||
|
||||
def _readonly_text(self, parent, content: str) -> tk.Text:
|
||||
wrap = ttk.Frame(parent)
|
||||
@@ -1065,6 +1220,9 @@ class Md2GostApp:
|
||||
def _browse_assignment(self) -> None:
|
||||
self._pick_into(self.assignment_var, "Бланк задания", [("Word", "*.docx")])
|
||||
|
||||
def _browse_styles(self) -> None:
|
||||
self._pick_into(self.styles_var, "Стили JSON", [("JSON", "*.json"), ("Все файлы", "*.*")])
|
||||
|
||||
def _browse_jar(self) -> None:
|
||||
self._pick_into(self.plantuml_var, "Свой plantuml.jar", [("JAR", "*.jar"), ("Все файлы", "*.*")])
|
||||
self._refresh_diagram_status()
|
||||
@@ -1149,6 +1307,7 @@ class Md2GostApp:
|
||||
self.template_var.set(req.template or "")
|
||||
self.title_var.set(req.title or "")
|
||||
self.assignment_var.set(req.assignment or "")
|
||||
self.styles_var.set(getattr(req, "styles_path", None) or "")
|
||||
self.plantuml_var.set(req.plantuml_jar or "")
|
||||
self.kroki_var.set(req.kroki_url or "")
|
||||
self._refresh_diagram_status()
|
||||
@@ -1181,6 +1340,7 @@ class Md2GostApp:
|
||||
diagram_fallback=_combo_key(self.fallback_var.get(), ("remote", "local", "off"), "remote"),
|
||||
diagram_format=_combo_key(self.format_var.get(), ("png", "svg"), "png"),
|
||||
diagram_scale=self._parse_diagram_scale(),
|
||||
styles_path=self.styles_var.get().strip() or None,
|
||||
debug=self.debug_var.get(),
|
||||
open_when_done=self.open_var.get(),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user