fixes
This commit is contained in:
147
docmost.log
147
docmost.log
@@ -140,3 +140,150 @@
|
||||
2026-04-11T21:27:43.593415Z INFO docmost_rust::api: search_pages: 1 results
|
||||
2026-04-11T21:27:43.650468Z INFO docmost_rust: search results: 1 items
|
||||
2026-04-11T21:28:04.431585Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:29:39.247050Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:29:47.431125Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
|
||||
2026-04-11T21:29:47.706907Z INFO docmost_rust: spaces loaded: 5 spaces
|
||||
2026-04-11T21:29:47.706967Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
|
||||
2026-04-11T21:29:47.984710Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:29:48.893873Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:29:49.325584Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:29:50.049917Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:29:50.442560Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:29:51.037887Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:29:51.830763Z INFO docmost_rust: pages loaded: 3 pages
|
||||
2026-04-11T21:29:58.166181Z INFO docmost_rust::api: fetch_page_content: POST https://docmost.nakano47.com/api/pages/info page_id=019ab71d-b515-7225-9d91-389a48b7c1db
|
||||
2026-04-11T21:29:58.410173Z INFO docmost_rust::api: fetch_page_content: status=200 OK body_len=886
|
||||
2026-04-11T21:29:58.410210Z INFO docmost_rust::api: fetch_page_content: raw body = {"data":{"id":"019ab71d-b515-7225-9d91-389a48b7c1db","slugId":"g7X1eTyFHz","title":null,"icon":null,"coverPhoto":null,"position":"a179C","parentPageId":null,"creatorId":"019a8369-7137-75b6-b171-cb7e11111fa7","lastUpdatedById":"019a8369-7137-75b6-b171-cb7e11111fa7","spaceId":"019aa2cc-4c7b-7408-a43a-6e17f03abc96","workspaceId":"019a8369-713a-7aee-b3d9-45fb478c1f81","isLocked":false,"createdAt":"2025-11-24T18:26:08.785Z","updatedAt":"2025-11-24T18:26:08.785Z","deletedAt":null,"contributorIds":[],"content":null,"creator":{"id":"019a8369-7137-75b6-b171-cb7e11111fa7","name":"arthur","avatarUrl":null},"lastUpdatedBy":{"id":"019a8369-7137-75b6-b171-cb7e11111fa7","name":"arthur","avatarUrl":null},"contributors":[],"space":{"id":"019aa2cc-4c7b-7408-a43a-6e17f03abc96","name":"Útil Links","slug":"TL"},"permissions":{"canEdit":true,"hasRestriction":false}},"success":true,"status":200}
|
||||
2026-04-11T21:29:58.410322Z INFO docmost_rust::api: fetch_page_content: top-level keys = Some(["data", "status", "success"])
|
||||
2026-04-11T21:29:58.410344Z INFO docmost_rust::api: fetch_page_content: extracted content len=0
|
||||
2026-04-11T21:29:58.418609Z INFO docmost_rust: page content loaded: id=019ab71d-b515-7225-9d91-389a48b7c1db title="Untitled" content_len=0
|
||||
2026-04-11T21:29:58.418653Z INFO docmost_rust: content preview: ""
|
||||
2026-04-11T21:29:58.418664Z INFO docmost_rust: textarea lines: 0
|
||||
2026-04-11T21:29:58.418673Z INFO docmost_rust: state → Editor
|
||||
2026-04-11T21:30:05.112248Z INFO docmost_rust::api: fetch_page_content: POST https://docmost.nakano47.com/api/pages/info page_id=019c3404-7aa7-7571-9ead-6d672d02cb23
|
||||
2026-04-11T21:30:05.400130Z INFO docmost_rust::api: fetch_page_content: status=200 OK body_len=1403
|
||||
2026-04-11T21:30:05.400209Z INFO docmost_rust::api: fetch_page_content: raw body = {"data":{"id":"019c3404-7aa7-7571-9ead-6d672d02cb23","slugId":"YjWODAMZOb","title":"Download list","icon":null,"coverPhoto":null,"position":"a26Jb","parentPageId":null,"creatorId":"019a8369-7137-75b6-b171-cb7e11111fa7","lastUpdatedById":"019a8369-7137-75b6-b171-cb7e11111fa7","spaceId":"019aa2cc-4c7b-7408-a43a-6e17f03abc96","workspaceId":"019a8369-713a-7aee-b3d9-45fb478c1f81","isLocked":false,"createdAt":"2026-02-06T17:33:54.663Z","updatedAt":"2026-02-06T17:34:18.052Z","deletedAt":null,"contributorIds":["019a8369-7137-75b6-b171-cb7e11111fa7"],"content":"https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqbG5lM3p1b0I5dXdtcXdER2l4T1FZN05sajVuZ3xBQ3Jtc0tuWklOVzRTZ2pXZmpQLWFWNnM5YUJKVXNlOWJCNERGWFUzOVN4bFJjS1RuU0xLRHg2c0tybjctWlFWdlFOTG9YTmRGT01DY2hmakd6REZJbHVtU082MG5qMlNfWGpVZFNWSExSMF92RDJjVF9DWWxOVQ&q=https%3A%2F%2Fdrive.google.com%2Ffile%2Fd%2F1aqUcK3cUB1hheswwrXQkKAWG65swElAA%2Fview%3Fusp%3Dsharing&v=9z2Z4Ul6PmQ","creator":{"id":"019a8369-7137-75b6-b171-cb7e11111fa7","name":"arthur","avatarUrl":null},"lastUpdatedBy":{"id":"019a8369-7137-75b6-b171-cb7e11111fa7","name":"arthur","avatarUrl":null},"contributors":[{"id":"019a8369-7137-75b6-b171-cb7e11111fa7","name":"arthur","avatarUrl":null}],"space":{"id":"019aa2cc-4c7b-7408-a43a-6e17f03abc96","name":"Útil Links","slug":"TL"},"permissions":{"canEdit":true,"hasRestriction":false}},"success":true,"status":200}
|
||||
2026-04-11T21:30:05.400453Z INFO docmost_rust::api: fetch_page_content: top-level keys = Some(["data", "status", "success"])
|
||||
2026-04-11T21:30:05.400492Z INFO docmost_rust::api: fetch_page_content: extracted content len=392
|
||||
2026-04-11T21:30:05.440594Z INFO docmost_rust: page content loaded: id=019c3404-7aa7-7571-9ead-6d672d02cb23 title="Download list" content_len=392
|
||||
2026-04-11T21:30:05.440682Z INFO docmost_rust: content preview: "https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqbG5lM3p1b0I5dXdtcXdER2l4T1FZN05sajVuZ3xBQ3Jtc0tuWklOVzRTZ2pXZmpQLWFWNnM5YUJKVXNlOWJCNERGWFUzOVN4bFJjS1RuU0xLRHg2c0tybjctWlF"
|
||||
2026-04-11T21:30:05.440742Z INFO docmost_rust: textarea lines: 1
|
||||
2026-04-11T21:30:05.440769Z INFO docmost_rust: state → Editor
|
||||
2026-04-11T21:30:30.910843Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:30:31.089038Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:30:32.712319Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:30:33.363095Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:30:33.872913Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:30:34.487430Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:30:35.118020Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:30:35.987499Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:30:37.374189Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:30:38.193984Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:30:39.688846Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:30:40.681715Z INFO docmost_rust: pages loaded: 3 pages
|
||||
2026-04-11T21:30:41.394126Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:30:42.090226Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:30:42.600549Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:30:43.634279Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:30:43.807572Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:30:44.401381Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:30:44.682769Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:30:44.890249Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:30:45.401689Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:30:45.613548Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:30:45.727369Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:30:45.889612Z INFO docmost_rust: pages loaded: 3 pages
|
||||
2026-04-11T21:30:46.378793Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:30:46.493106Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:30:46.701079Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:30:46.820834Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:35:13.428450Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:35:13.603118Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:35:13.915433Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:35:14.093044Z INFO docmost_rust: pages loaded: 3 pages
|
||||
2026-04-11T21:35:14.825084Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:35:15.012750Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:35:15.231169Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:38:33.382406Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:38:45.588348Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
|
||||
2026-04-11T21:38:45.875223Z INFO docmost_rust: spaces loaded: 5 spaces
|
||||
2026-04-11T21:38:45.875293Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
|
||||
2026-04-11T21:38:46.204359Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:38:47.585723Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:38:47.974573Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:38:50.567544Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:38:51.146561Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:38:51.725761Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:38:52.474459Z INFO docmost_rust: pages loaded: 3 pages
|
||||
2026-04-11T21:38:55.035281Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:38:57.256724Z INFO docmost_rust::api: fetch_page_content: POST https://docmost.nakano47.com/api/pages/info page_id=019ac0d0-c5d5-7e66-ae14-3014e35fbb92
|
||||
2026-04-11T21:38:57.551645Z INFO docmost_rust::api: fetch_page_content: status=200 OK body_len=2067
|
||||
2026-04-11T21:38:57.551731Z INFO docmost_rust::api: fetch_page_content: raw body = {"data":{"id":"019ac0d0-c5d5-7e66-ae14-3014e35fbb92","slugId":"Gyb4Wq5OSs","title":"NextJS 16 midudev","icon":null,"coverPhoto":null,"position":"a19xB","parentPageId":null,"creatorId":"019a8369-7137-75b6-b171-cb7e11111fa7","lastUpdatedById":"019a8369-7137-75b6-b171-cb7e11111fa7","spaceId":"019a948a-071a-7425-b427-78832d0f29f3","workspaceId":"019a8369-713a-7aee-b3d9-45fb478c1f81","isLocked":false,"createdAt":"2025-11-26T15:38:18.962Z","updatedAt":"2025-11-26T17:38:30.842Z","deletedAt":null,"contributorIds":["019a8369-7137-75b6-b171-cb7e11111fa7"],"content":"https://nextjs.org/docs\n\nhttps://www.youtube.com/watch?v=VosHdPQr6nI\n\n```\npnpx create-next-app@latest\n```\n\nesto crea todos los componentes por defecto, al darle que si.\n\nhttp://ui.shadcn.com/docs/components/\n\nen esa pagina se pueden encontrar componentes para no tener que hacerlos\n\n```\npnpx shadcn@latest init\npnpx shadcn@latest add card label input button \npnpm dev\n```\n\nCon este comando agregamos los componentes necesarios para el proyecto.\n\nAhora vamos a crear el backend, con strapi\n\n```\npnpx create-strapi-app@latest backend\npnpm approve-builds\n```\n\nluego contestar las preguntas que hace, no es necesario hacer login.\n\nluego de hacer el run develop se desplegara una pagina para registrarse en localhost. \nluego de llenar el formulario se hay que darle a Content-Type Builder\n\nCreate a single type\n\n\n\nluego se pueden agregar cosas\n\n_Strapi es una forma facil y rapida de crear APIS, con roles usuarios etc y tambien ofrece diferentes plugins_","creator":{"id":"019a8369-7137-75b6-b171-cb7e11111fa7","name":"arthur","avatarUrl":null},"lastUpdatedBy":{"id":"019a8369-7137-75b6-b171-cb7e11111fa7","name":"arthur","avatarUrl":null},"contributors":[{"id":"019a8369-7137-75b6-b171-cb7e11111fa7","name":"arthur","avatarUrl":null}],"space":{"id":"019a948a-071a-7425-b427-78832d0f29f3","name":"NextJS","slug":"nextjs"},"permissions":{"canEdit":true,"hasRestriction":false}},"success":true,"status":200}
|
||||
2026-04-11T21:38:57.552092Z INFO docmost_rust::api: fetch_page_content: top-level keys = Some(["data", "status", "success"])
|
||||
2026-04-11T21:38:57.552194Z INFO docmost_rust::api: fetch_page_content: extracted content len=1013
|
||||
2026-04-11T21:38:57.554197Z INFO docmost_rust: page content loaded: id=019ac0d0-c5d5-7e66-ae14-3014e35fbb92 title="NextJS 16 midudev" content_len=1013
|
||||
2026-04-11T21:38:57.554259Z INFO docmost_rust: content preview: "https://nextjs.org/docs\n\nhttps://www.youtube.com/watch?v=VosHdPQr6nI\n\n```\npnpx create-next-app@latest\n```\n\nesto crea todos los componentes por defecto, al darle que si.\n\nhttp://ui.shadcn.com/docs/comp"
|
||||
2026-04-11T21:38:57.554817Z INFO docmost_rust: textarea lines: 41
|
||||
2026-04-11T21:38:57.554845Z INFO docmost_rust: state → Editor
|
||||
2026-04-11T21:40:04.606686Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:40:10.031362Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
|
||||
2026-04-11T21:40:10.307498Z INFO docmost_rust: spaces loaded: 5 spaces
|
||||
2026-04-11T21:40:10.307547Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
|
||||
2026-04-11T21:40:10.633254Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:40:11.444021Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:40:11.912537Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:40:12.726953Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:40:13.367085Z INFO docmost_rust: pages loaded: 3 pages
|
||||
2026-04-11T21:40:15.001191Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:40:15.282960Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:40:15.602258Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:40:15.882600Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:45:15.235276Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:45:19.977051Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
|
||||
2026-04-11T21:45:20.313747Z INFO docmost_rust: spaces loaded: 5 spaces
|
||||
2026-04-11T21:45:20.313812Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
|
||||
2026-04-11T21:45:20.650391Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:45:22.308225Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:45:22.746924Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:45:23.559349Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:45:23.914877Z INFO docmost_rust: pages loaded: 3 pages
|
||||
2026-04-11T21:45:24.804748Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:45:25.039559Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:45:25.316099Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:45:25.667300Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:45:25.978557Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:45:26.154212Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:45:26.437992Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:45:27.079480Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:45:27.634946Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:45:50.121538Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:45:51.897924Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:45:57.189610Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
|
||||
2026-04-11T21:45:57.530089Z INFO docmost_rust: spaces loaded: 5 spaces
|
||||
2026-04-11T21:45:57.530152Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
|
||||
2026-04-11T21:45:57.812070Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:46:06.541183Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:46:06.839324Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:46:06.998234Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:46:07.453598Z INFO docmost_rust: pages loaded: 3 pages
|
||||
2026-04-11T21:55:24.646993Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:56:00.568474Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:56:11.943220Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:56:14.296222Z INFO docmost_rust: login error: Cannot POST /auth/login
|
||||
2026-04-11T21:56:27.622900Z INFO docmost_rust: login error: Cannot POST /auth/login
|
||||
2026-04-11T21:56:31.879011Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
|
||||
2026-04-11T21:56:32.155496Z INFO docmost_rust: spaces loaded: 5 spaces
|
||||
2026-04-11T21:56:32.155567Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
|
||||
2026-04-11T21:56:32.476120Z INFO docmost_rust: pages loaded: 1 pages
|
||||
2026-04-11T21:56:33.189116Z INFO docmost_rust: pages loaded: 0 pages
|
||||
2026-04-11T21:56:33.398639Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:56:33.785215Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:56:34.383027Z INFO docmost_rust: pages loaded: 3 pages
|
||||
2026-04-11T21:56:35.160467Z INFO docmost_rust: pages loaded: 2 pages
|
||||
2026-04-11T21:56:35.479213Z INFO docmost_rust: pages loaded: 8 pages
|
||||
2026-04-11T21:56:49.755529Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:57:00.511114Z INFO docmost_rust: --- startup ---
|
||||
2026-04-11T21:57:03.860624Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
|
||||
2026-04-11T21:57:04.176895Z INFO docmost_rust: spaces loaded: 5 spaces
|
||||
2026-04-11T21:57:04.176933Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
|
||||
2026-04-11T21:57:04.493124Z INFO docmost_rust: pages loaded: 1 pages
|
||||
|
||||
11
src/app.rs
11
src/app.rs
@@ -85,10 +85,15 @@ pub struct LoginForm {
|
||||
impl LoginForm {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
url: String::from("https://docmost.nakano47.com"),
|
||||
email: String::from("chamagua1@proton.me"),
|
||||
url: std::env::var("DOCMOST_URL")
|
||||
.unwrap_or_else(|_| "http://localhost:3000".to_string()),
|
||||
email: std::env::var("DOCMOST_EMAIL").unwrap_or_default(),
|
||||
password: String::new(),
|
||||
active_field: LoginField::Email,
|
||||
active_field: if std::env::var("DOCMOST_EMAIL").is_ok() {
|
||||
LoginField::Password
|
||||
} else {
|
||||
LoginField::Email
|
||||
},
|
||||
error: None,
|
||||
submitting: false,
|
||||
}
|
||||
|
||||
169
src/ui.rs
169
src/ui.rs
@@ -7,19 +7,28 @@ use ratatui::{
|
||||
};
|
||||
use tui_textarea::TextArea;
|
||||
|
||||
use crate::app::{EditorFocus, EditorStatus, EditorView, LoginField, LoginForm, MainView, Panel, SearchView};
|
||||
use crate::app::{
|
||||
EditorFocus, EditorStatus, EditorView, LoginField, LoginForm, MainView, Panel, SearchView,
|
||||
};
|
||||
|
||||
// ─── Login screen ─────────────────────────────────────────────────────────────
|
||||
|
||||
pub fn draw_login(f: &mut Frame, login: &LoginForm) {
|
||||
let size = f.size();
|
||||
|
||||
f.render_widget(Block::default().style(Style::default().bg(Color::Black)), size);
|
||||
f.render_widget(
|
||||
Block::default().style(Style::default().bg(Color::Black)),
|
||||
size,
|
||||
);
|
||||
|
||||
let dialog = centered_rect(50, 18, size);
|
||||
f.render_widget(Clear, dialog);
|
||||
|
||||
let title = if login.submitting { "Logging in…" } else { "Login — Docmost" };
|
||||
let title = if login.submitting {
|
||||
"Logging in…"
|
||||
} else {
|
||||
"Login — Docmost"
|
||||
};
|
||||
f.render_widget(
|
||||
Block::default()
|
||||
.title(title)
|
||||
@@ -46,9 +55,30 @@ pub fn draw_login(f: &mut Frame, login: &LoginForm) {
|
||||
])
|
||||
.split(inner);
|
||||
|
||||
render_field(f, "Server URL", &login.url, false, login.active_field == LoginField::Url, rows[0]);
|
||||
render_field(f, "Email", &login.email, false, login.active_field == LoginField::Email, rows[1]);
|
||||
render_field(f, "Password", &login.password, true, login.active_field == LoginField::Password, rows[2]);
|
||||
render_field(
|
||||
f,
|
||||
"Server URL",
|
||||
&login.url,
|
||||
false,
|
||||
login.active_field == LoginField::Url,
|
||||
rows[0],
|
||||
);
|
||||
render_field(
|
||||
f,
|
||||
"Email",
|
||||
&login.email,
|
||||
false,
|
||||
login.active_field == LoginField::Email,
|
||||
rows[1],
|
||||
);
|
||||
render_field(
|
||||
f,
|
||||
"Password",
|
||||
&login.password,
|
||||
true,
|
||||
login.active_field == LoginField::Password,
|
||||
rows[2],
|
||||
);
|
||||
|
||||
let hint = if let Some(err) = &login.error {
|
||||
Paragraph::new(Line::from(Span::styled(
|
||||
@@ -74,7 +104,11 @@ fn render_field(
|
||||
active: bool,
|
||||
area: Rect,
|
||||
) {
|
||||
let display = if masked { "*".repeat(value.len()) } else { value.to_string() };
|
||||
let display = if masked {
|
||||
"*".repeat(value.len())
|
||||
} else {
|
||||
value.to_string()
|
||||
};
|
||||
let content = format!("{display}{}", if active { "▌" } else { "" });
|
||||
let border_style = if active {
|
||||
Style::default().fg(Color::Cyan)
|
||||
@@ -82,8 +116,12 @@ fn render_field(
|
||||
Style::default().fg(Color::DarkGray)
|
||||
};
|
||||
f.render_widget(
|
||||
Paragraph::new(content)
|
||||
.block(Block::default().title(label).borders(Borders::ALL).border_style(border_style)),
|
||||
Paragraph::new(content).block(
|
||||
Block::default()
|
||||
.title(label)
|
||||
.borders(Borders::ALL)
|
||||
.border_style(border_style),
|
||||
),
|
||||
area,
|
||||
);
|
||||
}
|
||||
@@ -105,12 +143,22 @@ pub fn draw_main(f: &mut Frame, main: &MainView) {
|
||||
|
||||
// ── Spaces panel (top-left) ──
|
||||
let spaces_focused = main.focus == Panel::Spaces;
|
||||
let spaces_title = if main.loading_spaces { "Spaces (loading…)" } else { "Spaces" };
|
||||
let spaces_title = if main.loading_spaces {
|
||||
"Spaces (loading…)"
|
||||
} else {
|
||||
"Spaces"
|
||||
};
|
||||
|
||||
let space_items: Vec<ListItem> = if main.spaces.is_empty() && !main.loading_spaces {
|
||||
vec![ListItem::new(Span::styled("No spaces", Style::default().fg(Color::DarkGray)))]
|
||||
vec![ListItem::new(Span::styled(
|
||||
"No spaces",
|
||||
Style::default().fg(Color::DarkGray),
|
||||
))]
|
||||
} else {
|
||||
main.spaces.iter().map(|s| ListItem::new(s.name.as_str())).collect()
|
||||
main.spaces
|
||||
.iter()
|
||||
.map(|s| ListItem::new(s.name.as_str()))
|
||||
.collect()
|
||||
};
|
||||
|
||||
f.render_stateful_widget(
|
||||
@@ -121,7 +169,11 @@ pub fn draw_main(f: &mut Frame, main: &MainView) {
|
||||
.borders(Borders::ALL)
|
||||
.border_style(panel_border(spaces_focused)),
|
||||
)
|
||||
.highlight_style(Style::default().bg(Color::Blue).add_modifier(Modifier::BOLD))
|
||||
.highlight_style(
|
||||
Style::default()
|
||||
.bg(Color::Blue)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)
|
||||
.highlight_symbol("▶ "),
|
||||
left_layout[0],
|
||||
&mut list_state(main.selected_space),
|
||||
@@ -129,10 +181,17 @@ pub fn draw_main(f: &mut Frame, main: &MainView) {
|
||||
|
||||
// ── Pages panel (bottom-left) ──
|
||||
let pages_focused = main.focus == Panel::Pages;
|
||||
let pages_title = if main.loading_pages { "Pages (loading…)" } else { "Pages" };
|
||||
let pages_title = if main.loading_pages {
|
||||
"Pages (loading…)"
|
||||
} else {
|
||||
"Pages"
|
||||
};
|
||||
|
||||
let page_items: Vec<ListItem> = if main.pages.is_empty() && !main.loading_pages {
|
||||
vec![ListItem::new(Span::styled("No pages", Style::default().fg(Color::DarkGray)))]
|
||||
vec![ListItem::new(Span::styled(
|
||||
"No pages",
|
||||
Style::default().fg(Color::DarkGray),
|
||||
))]
|
||||
} else {
|
||||
main.pages
|
||||
.iter()
|
||||
@@ -148,7 +207,11 @@ pub fn draw_main(f: &mut Frame, main: &MainView) {
|
||||
.borders(Borders::ALL)
|
||||
.border_style(panel_border(pages_focused)),
|
||||
)
|
||||
.highlight_style(Style::default().bg(Color::Blue).add_modifier(Modifier::BOLD))
|
||||
.highlight_style(
|
||||
Style::default()
|
||||
.bg(Color::Blue)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)
|
||||
.highlight_symbol("▶ "),
|
||||
left_layout[1],
|
||||
&mut list_state(main.selected_page),
|
||||
@@ -160,10 +223,18 @@ pub fn draw_main(f: &mut Frame, main: &MainView) {
|
||||
} else if let Some(err) = &main.error {
|
||||
format!("Error: {err}")
|
||||
} else {
|
||||
let space_name = main.spaces.get(main.selected_space).map(|s| s.name.as_str()).unwrap_or("-");
|
||||
let page_title = main.pages.get(main.selected_page).and_then(|p| p.title.as_deref()).unwrap_or("-");
|
||||
let space_name = main
|
||||
.spaces
|
||||
.get(main.selected_space)
|
||||
.map(|s| s.name.as_str())
|
||||
.unwrap_or("-");
|
||||
let page_title = main
|
||||
.pages
|
||||
.get(main.selected_page)
|
||||
.and_then(|p| p.title.as_deref())
|
||||
.unwrap_or("-");
|
||||
format!(
|
||||
"Space: {space_name}\nPage: {page_title}\n\nEnter open editor\nTab switch panel\n↑↓/j k navigate\nq/Esc quit"
|
||||
"Space: {space_name}\nPage: {page_title}\n\n_______________________\nEnter open editor\nTab switch panel\n↑↓/j k navigate\nq/Esc quit\n//Ctrl+F search"
|
||||
)
|
||||
};
|
||||
|
||||
@@ -202,8 +273,12 @@ pub fn draw_editor(f: &mut Frame, editor: &EditorView, textarea: &mut TextArea<'
|
||||
if title_focused { "▌" } else { "" }
|
||||
);
|
||||
f.render_widget(
|
||||
Paragraph::new(title_display)
|
||||
.block(Block::default().title(" Title ").borders(Borders::ALL).border_style(title_border)),
|
||||
Paragraph::new(title_display).block(
|
||||
Block::default()
|
||||
.title(" Title ")
|
||||
.borders(Borders::ALL)
|
||||
.border_style(title_border),
|
||||
),
|
||||
layout[0],
|
||||
);
|
||||
|
||||
@@ -229,15 +304,21 @@ pub fn draw_editor(f: &mut Frame, editor: &EditorView, textarea: &mut TextArea<'
|
||||
// ── Status bar ──
|
||||
let status_line = match &editor.status {
|
||||
Some(EditorStatus::Saved) => Line::from(vec![
|
||||
Span::styled(" Saved! ", Style::default().fg(Color::Green).add_modifier(Modifier::BOLD)),
|
||||
Span::styled(" Ctrl+S: Save · Tab: Switch field · Esc: Back", Style::default().fg(Color::DarkGray)),
|
||||
]),
|
||||
Some(EditorStatus::Error(e)) => Line::from(vec![
|
||||
Span::styled(
|
||||
format!(" Error: {e} "),
|
||||
Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
|
||||
" Saved! ",
|
||||
Style::default()
|
||||
.fg(Color::Green)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
),
|
||||
Span::styled(
|
||||
" Ctrl+S: Save · Tab: Switch field · Esc: Back",
|
||||
Style::default().fg(Color::DarkGray),
|
||||
),
|
||||
]),
|
||||
Some(EditorStatus::Error(e)) => Line::from(vec![Span::styled(
|
||||
format!(" Error: {e} "),
|
||||
Style::default().fg(Color::Red).add_modifier(Modifier::BOLD),
|
||||
)]),
|
||||
None => Line::from(Span::styled(
|
||||
" Ctrl+S: Save · Tab: Switch field · Esc: Back",
|
||||
Style::default().fg(Color::DarkGray),
|
||||
@@ -262,7 +343,11 @@ pub fn draw_search(f: &mut Frame, search: &SearchView) {
|
||||
|
||||
// ── Query input ──
|
||||
let query_display = format!("{}▌", search.query);
|
||||
let query_title = if search.loading { " Search (searching…) " } else { " Search " };
|
||||
let query_title = if search.loading {
|
||||
" Search (searching…) "
|
||||
} else {
|
||||
" Search "
|
||||
};
|
||||
f.render_widget(
|
||||
Paragraph::new(query_display).block(
|
||||
Block::default()
|
||||
@@ -281,8 +366,15 @@ pub fn draw_search(f: &mut Frame, search: &SearchView) {
|
||||
|
||||
// Results list
|
||||
let result_items: Vec<ListItem> = if search.results.is_empty() && !search.loading {
|
||||
let msg = if search.query.is_empty() { "Type to search…" } else { "No results" };
|
||||
vec![ListItem::new(Span::styled(msg, Style::default().fg(Color::DarkGray)))]
|
||||
let msg = if search.query.is_empty() {
|
||||
"Type to search…"
|
||||
} else {
|
||||
"No results"
|
||||
};
|
||||
vec![ListItem::new(Span::styled(
|
||||
msg,
|
||||
Style::default().fg(Color::DarkGray),
|
||||
))]
|
||||
} else {
|
||||
search
|
||||
.results
|
||||
@@ -306,7 +398,11 @@ pub fn draw_search(f: &mut Frame, search: &SearchView) {
|
||||
.borders(Borders::ALL)
|
||||
.border_style(Style::default().fg(Color::White)),
|
||||
)
|
||||
.highlight_style(Style::default().bg(Color::Blue).add_modifier(Modifier::BOLD))
|
||||
.highlight_style(
|
||||
Style::default()
|
||||
.bg(Color::Blue)
|
||||
.add_modifier(Modifier::BOLD),
|
||||
)
|
||||
.highlight_symbol("▶ "),
|
||||
mid[0],
|
||||
&mut list_state(search.selected),
|
||||
@@ -319,7 +415,11 @@ pub fn draw_search(f: &mut Frame, search: &SearchView) {
|
||||
format!("Error: {err}")
|
||||
} else if let Some(result) = search.results.get(search.selected) {
|
||||
let title = result.title.as_deref().unwrap_or("Untitled");
|
||||
let space = result.space.as_ref().map(|s| s.name.as_str()).unwrap_or("-");
|
||||
let space = result
|
||||
.space
|
||||
.as_ref()
|
||||
.map(|s| s.name.as_str())
|
||||
.unwrap_or("-");
|
||||
let highlight = result
|
||||
.highlight
|
||||
.as_deref()
|
||||
@@ -348,7 +448,10 @@ pub fn draw_search(f: &mut Frame, search: &SearchView) {
|
||||
|
||||
// ── Hint bar ──
|
||||
let hint = if search.opening_page {
|
||||
Line::from(Span::styled(" Opening…", Style::default().fg(Color::Yellow)))
|
||||
Line::from(Span::styled(
|
||||
" Opening…",
|
||||
Style::default().fg(Color::Yellow),
|
||||
))
|
||||
} else {
|
||||
Line::from(Span::styled(
|
||||
" ↑↓: navigate · Enter: open · Esc: back",
|
||||
|
||||
68
todo.txt
68
todo.txt
@@ -1,39 +1,47 @@
|
||||
can we implement Full-text search across pages ?
|
||||
/btwhow is the process to install this program as a command on linux
|
||||
to type "$ docmost-rust" on the console and the program starts,
|
||||
also how to add env variables for $baseurl $email to
|
||||
automatically load this values on the login screen
|
||||
|
||||
Yes, totally doable. Here's the honest breakdown:
|
||||
|
||||
What the Docmost API offers
|
||||
cargo build --release
|
||||
sudo cp target/release/docmost-rust /usr/local/bin/docmost-rust
|
||||
|
||||
Looking at the sidebar-pages endpoint pattern, Docmost likely has a
|
||||
search endpoint — probably something like POST /search with a {
|
||||
query: string, spaceId?: string } body. This would be the right
|
||||
approach rather than fetching all pages and filtering client-side.
|
||||
That's it. Any user on the system can now run docmost-rust from any
|
||||
directory.
|
||||
|
||||
TUI implementation
|
||||
If you prefer a user-local install (no sudo):
|
||||
cargo install --path .
|
||||
This puts the binary in ~/.cargo/bin/, which is already in your
|
||||
$PATH if you installed Rust via rustup.
|
||||
|
||||
A search screen would work like this:
|
||||
---
|
||||
Environment variables for pre-filling login
|
||||
|
||||
┌─ Search ─────────────────────────────────┐
|
||||
│ query▌ │ ← type to search
|
||||
├──────────────────────────────────────────┤
|
||||
│ ▶ Result page title Space name │
|
||||
│ Another matching page Other space │
|
||||
│ ... │
|
||||
└──────────────────────────────────────────┘
|
||||
Enter: open · Esc: back
|
||||
Add these to your ~/.bashrc, ~/.zshrc, or
|
||||
~/.config/fish/config.fish:
|
||||
|
||||
- Trigger it with / or Ctrl+F from the main view
|
||||
- Each keypress fires a debounced API call (e.g. wait 300ms after
|
||||
last keystroke)
|
||||
- Results show page title + space name
|
||||
- Enter opens the selected result in the editor
|
||||
- Esc goes back
|
||||
bash/zsh:
|
||||
export DOCMOST_URL="https://docmost.nakano47.com"
|
||||
export DOCMOST_EMAIL="chamagua1@proton.me"
|
||||
|
||||
The debounce part is the only tricky bit in the async loop — you'd
|
||||
track a last_typed: Instant and only fire the search if elapsed() >
|
||||
300ms without new input.
|
||||
fish:
|
||||
set -x DOCMOST_URL "https://docmost.nakano47.com"
|
||||
set -x DOCMOST_EMAIL "chamagua1@proton.me"
|
||||
|
||||
Caveat: if Docmost's API doesn't expose a search endpoint, you'd
|
||||
have to fetch all pages across all spaces and do client-side string
|
||||
matching, which is slow and not full-text. Worth checking the API
|
||||
docs first to confirm the endpoint exists before building it.
|
||||
Then in the Rust code, replace the hardcoded defaults in
|
||||
LoginForm::new() inside src/app.rs:
|
||||
|
||||
impl LoginForm {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
url: std::env::var("DOCMOST_URL")
|
||||
.unwrap_or_else(|_|
|
||||
"http://localhost:3000".to_string()),
|
||||
email: std::env::var("DOCMOST_EMAIL")
|
||||
.unwrap_or_default(),
|
||||
password: String::new(),
|
||||
// ...
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user