Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/en/12_GUIDED_WORKFLOW_TOUR.md
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,7 @@ Common examples:
- experiment summaries
- result reports
- paper drafts
- LaTeX source files and BibTeX references

In practice, many users treat Markdown files in the quest as a private local-first notebook for:

Expand All @@ -340,6 +341,14 @@ In practice, many users treat Markdown files in the quest as a private local-fir
- findings
- team coordination

When you open a LaTeX project folder, the browser editor treats the folder as one LaTeX workspace. Source files such as `main.tex`, chapter files under subfolders, BibTeX files, and style files switch inside the same editor, Overleaf-style, instead of spawning separate top-level editors or internal source tabs. The file picker still lists the full project source tree for quick switching.

The editor auto-saves source edits shortly after you type. Background autosaves only persist the source; they do not start PDF compilation. Manual saves default to compile-on-save: `Ctrl/Cmd+S` or the `Save` button saves the active LaTeX file and then starts one PDF compilation when the save succeeds. `Save & Compile` remains available for an explicit compile action and still saves the current source before starting PDF compilation.

If another process changes the active LaTeX source file while it is open, such as an AI edit or terminal command, the editor refreshes automatically when the local buffer has no unsaved edits. If the local buffer is dirty, autosave pauses and the editor asks you to either reload the external version or explicitly overwrite it, so an ordinary save cannot silently replace external changes.

After a successful compile, the PDF preview uses SyncTeX metadata when available. Double-click a rendered PDF word to jump back to the matching LaTeX source file and select the corresponding source token; the editor uses the PDF word box plus multiple SyncTeX samples to avoid broad line-level selections. Older builds without SyncTeX data need to be recompiled before PDF-to-source jumps are available.

### 6.5 Canvas

Canvas makes the research map visible.
Expand Down
9 changes: 9 additions & 0 deletions docs/zh/12_GUIDED_WORKFLOW_TOUR.md
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,7 @@ Explorer 是 quest 的文件视角。
- 实验总结
- 结果报告
- 论文草稿
- LaTeX 源文件与 BibTeX 引用

很多用户会把 quest 里的 Markdown 文件当作一个本地优先、类似 Notion 的私有笔记本,用来记录:

Expand All @@ -338,6 +339,14 @@ Explorer 是 quest 的文件视角。
- 发现
- 协作信息

打开 LaTeX 项目文件夹时,浏览器编辑器会把该文件夹作为一个统一的 LaTeX 工作区处理。`main.tex`、章节子目录中的 `.tex` 文件、BibTeX 文件和样式文件会像 Overleaf 一样在同一个编辑器里直接切换,而不是为每个文件创建新的顶层编辑器或内部源码标签页。文件选择器仍会列出完整项目源码树,方便快速切换。

编辑器会在输入后短时间内自动保存源文件。后台自动保存只负责落盘源码,不会启动 PDF 编译。手动保存默认开启保存后自动编译:`Ctrl/Cmd+S` 或 `保存` 按钮会先保存当前 LaTeX 文件,保存成功后启动一次 PDF 编译。`保存并编译` 仍可用于显式编译,并会先保存当前源码,再启动 PDF 编译。

如果其它进程在当前 LaTeX 源文件打开期间修改了它,例如 AI 编辑或终端命令,且本地缓冲区没有未保存内容,编辑器会自动刷新到外部版本。若本地缓冲区已被修改,自动保存会暂停,并提示你选择重新载入外部版本或明确覆盖外部版本,避免普通保存静默覆盖外部修改。

成功编译后,PDF 预览会在可用时使用 SyncTeX 元数据。双击 PDF 中渲染出的某个单词时,编辑器会结合 PDF 单词框和多点 SyncTeX 采样跳转到匹配的 LaTeX 源文件,并选中对应的源码 token,避免退化成大范围行级选中。没有 SyncTeX 数据的旧构建需要重新编译后才能使用 PDF 到源码跳转。

### 6.5 Canvas

Canvas 会把研究地图直接展示出来。
Expand Down
20 changes: 19 additions & 1 deletion src/deepscientist/daemon/api/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2046,7 +2046,7 @@ def document_save(self, quest_id: str, document_id: str, body: dict) -> dict:
try:
return self.app.quest_service.save_document(
quest_id,
document_id,
unquote(document_id),
body["content"],
previous_revision=body.get("revision"),
)
Expand Down Expand Up @@ -2121,6 +2121,9 @@ def latex_compile(self, project_id: str, folder_id: str, body: dict) -> dict:
auto=body.get("auto"),
)

def latex_manifest(self, project_id: str, folder_id: str) -> dict:
return self.app.latex_service.manifest(project_id, folder_id)

def latex_builds(self, project_id: str, folder_id: str, path: str) -> list[dict]:
query = self.parse_query(path)
limit_raw = ((query.get("limit") or ["10"])[0] or "10").strip()
Expand All @@ -2133,6 +2136,21 @@ def latex_builds(self, project_id: str, folder_id: str, path: str) -> list[dict]
def latex_build(self, project_id: str, folder_id: str, build_id: str) -> dict:
return self.app.latex_service.get_build(project_id, folder_id, build_id)

def latex_synctex_edit(self, project_id: str, folder_id: str, build_id: str, body: dict) -> dict:
return self.app.latex_service.synctex_edit(
project_id,
folder_id,
build_id,
page=body.get("page"),
x=body.get("x"),
y=body.get("y"),
pdf_word=body.get("pdf_word"),
pdf_context_words=body.get("pdf_context_words"),
pdf_context_index=body.get("pdf_context_index"),
pdf_word_bbox=body.get("pdf_word_bbox"),
pdf_word_center=body.get("pdf_word_center"),
)

def latex_build_pdf(self, project_id: str, folder_id: str, build_id: str) -> tuple[int, dict, bytes]:
payload, file_name = self.app.latex_service.get_build_pdf(project_id, folder_id, build_id)
headers = {
Expand Down
2 changes: 2 additions & 0 deletions src/deepscientist/daemon/api/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,10 @@
("DELETE", re.compile(r"^/api/v1/annotations/(?P<annotation_id>[^/]+)$"), "annotation_delete"),
("POST", re.compile(r"^/api/v1/projects/(?P<project_id>[^/]+)/latex/init$"), "latex_init"),
("POST", re.compile(r"^/api/v1/projects/(?P<project_id>[^/]+)/latex/(?P<folder_id>[^/]+)/compile$"), "latex_compile"),
("GET", re.compile(r"^/api/v1/projects/(?P<project_id>[^/]+)/latex/(?P<folder_id>[^/]+)/manifest$"), "latex_manifest"),
("GET", re.compile(r"^/api/v1/projects/(?P<project_id>[^/]+)/latex/(?P<folder_id>[^/]+)/builds$"), "latex_builds"),
("GET", re.compile(r"^/api/v1/projects/(?P<project_id>[^/]+)/latex/(?P<folder_id>[^/]+)/builds/(?P<build_id>[^/]+)$"), "latex_build"),
("POST", re.compile(r"^/api/v1/projects/(?P<project_id>[^/]+)/latex/(?P<folder_id>[^/]+)/builds/(?P<build_id>[^/]+)/synctex/edit$"), "latex_synctex_edit"),
("GET", re.compile(r"^/api/v1/projects/(?P<project_id>[^/]+)/latex/(?P<folder_id>[^/]+)/builds/(?P<build_id>[^/]+)/pdf$"), "latex_build_pdf"),
("GET", re.compile(r"^/api/v1/projects/(?P<project_id>[^/]+)/latex/(?P<folder_id>[^/]+)/builds/(?P<build_id>[^/]+)/log$"), "latex_build_log"),
("GET", re.compile(r"^/api/v1/projects/(?P<project_id>[^/]+)/latex/(?P<folder_id>[^/]+)/archive$"), "latex_archive"),
Expand Down
2 changes: 1 addition & 1 deletion src/deepscientist/daemon/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -8799,7 +8799,7 @@ def _dispatch(self, method: str) -> None:
"repair_create",
"repair_close",
"hardware_update",
} or route_name in {"document_open", "document_asset_upload", "quest_file_create_folder", "quest_file_upload", "quest_file_rename", "quest_file_move", "quest_file_delete", "chat_upload_create", "chat_upload_delete", "chat", "command", "quest_control", "quest_message_read_now", "quest_message_withdraw", "config_save", "quest_create", "quest_baseline_binding", "run_create", "qq_inbound", "connector_inbound", "docs_open", "bash_stop", "quest_settings", "quest_bindings", "quest_delete", "quest_layout_update", "terminal_session_ensure", "terminal_attach", "terminal_input", "stage_view", "latex_init", "latex_compile", "system_update_action", "weixin_login_qr_start", "weixin_login_qr_wait", "arxiv_import", "annotation_create", "auth_login", "auth_rotate"}:
} or route_name in {"document_open", "document_asset_upload", "quest_file_create_folder", "quest_file_upload", "quest_file_rename", "quest_file_move", "quest_file_delete", "chat_upload_create", "chat_upload_delete", "chat", "command", "quest_control", "quest_message_read_now", "quest_message_withdraw", "config_save", "quest_create", "quest_baseline_binding", "run_create", "qq_inbound", "connector_inbound", "docs_open", "bash_stop", "quest_settings", "quest_bindings", "quest_delete", "quest_layout_update", "terminal_session_ensure", "terminal_attach", "terminal_input", "stage_view", "latex_init", "latex_compile", "latex_synctex_edit", "system_update_action", "weixin_login_qr_start", "weixin_login_qr_wait", "arxiv_import", "annotation_create", "auth_login", "auth_rotate"}:
payload = result(**params, body=body)
elif route_name == "config_validate":
payload = result(body)
Expand Down
Loading