emojis pages

This commit is contained in:
2026-04-11 21:04:49 -06:00
parent 77ea4e29e6
commit 81d48c83cb
5 changed files with 315 additions and 43 deletions

1
cloudesession.txt Normal file
View File

@@ -0,0 +1 @@
claude --resume bdd4f60b-f6b5-443f-bf3d-37431563b83d

View File

@@ -0,0 +1,170 @@
2026-04-11T23:03:04.539494Z INFO docmost_rust: --- startup ---
2026-04-11T23:03:07.785962Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
2026-04-11T23:03:08.082232Z INFO docmost_rust: spaces loaded: 6 spaces
2026-04-11T23:03:08.082304Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
2026-04-11T23:03:08.437269Z INFO docmost_rust: pages loaded: 1 pages
2026-04-11T23:03:09.027215Z INFO docmost_rust: pages loaded: 0 pages
2026-04-11T23:03:09.606572Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:03:10.642385Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:03:11.268250Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:03:11.836616Z INFO docmost_rust: pages loaded: 4 pages
2026-04-11T23:37:44.486083Z INFO docmost_rust: --- startup ---
2026-04-11T23:37:47.356720Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
2026-04-11T23:37:47.683974Z INFO docmost_rust: spaces loaded: 6 spaces
2026-04-11T23:37:47.684027Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
2026-04-11T23:37:48.013301Z INFO docmost_rust: pages loaded: 1 pages
2026-04-11T23:37:48.817615Z INFO docmost_rust: pages loaded: 0 pages
2026-04-11T23:37:49.010324Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:37:49.248622Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:37:49.248670Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:37:49.302185Z INFO docmost_rust: pages loaded: 4 pages
2026-04-11T23:37:49.531798Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:37:49.689251Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:31.443881Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:31.717199Z INFO docmost_rust: pages loaded: 4 pages
2026-04-11T23:38:32.118970Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:32.298831Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:32.516668Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:38:33.227903Z INFO docmost_rust: pages loaded: 0 pages
2026-04-11T23:38:33.529053Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:38:33.696916Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:34.740986Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:35.324324Z INFO docmost_rust: pages loaded: 4 pages
2026-04-11T23:38:35.625053Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:35.797151Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:36.020576Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:38:36.184665Z INFO docmost_rust: pages loaded: 0 pages
2026-04-11T23:38:36.920825Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:38:37.166427Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:37.324917Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:38.049366Z INFO docmost_rust: pages loaded: 4 pages
2026-04-11T23:38:39.884753Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:40.052905Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:40.270656Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:38:40.432663Z INFO docmost_rust: pages loaded: 0 pages
2026-04-11T23:38:41.183511Z INFO docmost_rust: pages loaded: 1 pages
2026-04-11T23:38:41.921757Z INFO docmost_rust: pages loaded: 0 pages
2026-04-11T23:38:42.402590Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:38:42.547581Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:42.728118Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:42.912185Z INFO docmost_rust: pages loaded: 4 pages
2026-04-11T23:38:43.370457Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:43.631107Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:43.631173Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:38:43.652222Z INFO docmost_rust: pages loaded: 0 pages
2026-04-11T23:38:43.732312Z INFO docmost_rust: pages loaded: 1 pages
2026-04-11T23:38:45.417247Z INFO docmost_rust: pages loaded: 0 pages
2026-04-11T23:38:45.671324Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:38:45.873737Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:46.073367Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:46.297772Z INFO docmost_rust: pages loaded: 4 pages
2026-04-11T23:38:48.751998Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:49.614931Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:50.284865Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:38:50.716189Z INFO docmost_rust: pages loaded: 0 pages
2026-04-11T23:38:50.901566Z INFO docmost_rust: pages loaded: 1 pages
2026-04-11T23:38:52.247510Z INFO docmost_rust: pages loaded: 0 pages
2026-04-11T23:38:52.419332Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:38:52.680637Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:52.700330Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:38:52.719974Z INFO docmost_rust: pages loaded: 4 pages
2026-04-11T23:39:05.731166Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:39:06.533415Z INFO docmost_rust: pages loaded: 4 pages
2026-04-11T23:39:06.875832Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:39:07.057986Z INFO docmost_rust: pages loaded: 2 pages
2026-04-11T23:39:07.275337Z INFO docmost_rust: pages loaded: 8 pages
2026-04-11T23:39:13.205971Z INFO docmost_rust::api: fetch_page_content: POST https://docmost.nakano47.com/api/pages/info page_id=019d43de-1212-77c3-8970-cd550e325bb8
2026-04-11T23:39:13.548711Z INFO docmost_rust::api: fetch_page_content: status=200 OK body_len=1872
2026-04-11T23:39:13.548768Z INFO docmost_rust::api: fetch_page_content: raw body = {"data":{"id":"019d43de-1212-77c3-8970-cd550e325bb8","slugId":"1PUibWlxIz","title":"Tareas pendientes xxyxs","icon":null,"coverPhoto":null,"position":"Zz0gH","parentPageId":null,"creatorId":"019a8369-7137-75b6-b171-cb7e11111fa7","lastUpdatedById":"019a8369-7137-75b6-b171-cb7e11111fa7","spaceId":"019a8369-7142-7c46-89c4-5f3a65b16942","workspaceId":"019a8369-713a-7aee-b3d9-45fb478c1f81","isLocked":false,"createdAt":"2026-03-31T12:28:40.336Z","updatedAt":"2026-04-11T21:11:46.336Z","deletedAt":null,"contributorIds":["019a8369-7137-75b6-b171-cb7e11111fa7"],"content":"- [ ] Hacer limpieza en sala y en cuarto\n- [ ] comprar soda cáustica para baño\n- [x] poner dock de steam deck\n- [ ] Agregar CV a nextcloudmodificar CV ponerle los títulos de los puestos\n- [x] Hacer pase a prod de lo del SFTP\n- [x] Instalar nextcloud y office\n- [ ] pagarle a jonny y a daisy\n\nComprar :\n\n- [ ] Soda cáustica\n- [ ] espejo banio(opcional)\n- [ ] llaves para brenda\n- [ ] raqueta eléctrica\n- [ ] cable hdmix2\n- [ ] batería portátil\n\n# titulo 1\n\n## titulo 2\n\n### titulo3\n\n- lista 1\n- lista 2\n- lista 3\n\n1. uno\n2. dow\n3. tres\n4. cuatro\n\n---\n\ndivider\n\n> quote\n\n```c\nuse <stdin>\n//codigo de prueba en c++\n```\n\n| | | |\n| --- | --- | --- |\n| tabla head 1 | tabla head2 | tabla head3 |\n| content1 | content2 | content3 |\n| | content2.2 apoiasdpoi | |\n| | | |","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":"019a8369-7142-7c46-89c4-5f3a65b16942","name":"General","slug":"general"},"permissions":{"canEdit":true,"hasRestriction":false}},"success":true,"status":200}
2026-04-11T23:39:13.548960Z INFO docmost_rust::api: fetch_page_content: top-level keys = Some(["data", "status", "success"])
2026-04-11T23:39:13.548993Z INFO docmost_rust::api: fetch_page_content: extracted content len=802
2026-04-11T23:39:13.549767Z INFO docmost_rust: page content loaded: id=019d43de-1212-77c3-8970-cd550e325bb8 title="Tareas pendientes xxyxs" content_len=802
2026-04-11T23:39:13.549798Z INFO docmost_rust: content preview: "- [ ] Hacer limpieza en sala y en cuarto\n- [ ] comprar soda cáustica para baño\n- [x] poner dock de steam deck\n- [ ] Agregar CV a nextcloudmodificar CV ponerle los títulos de los puestos\n- [x] Hacer"
2026-04-11T23:39:13.549983Z INFO docmost_rust: textarea lines: 49
2026-04-11T23:39:13.550002Z INFO docmost_rust: state → Editor
2026-04-12T00:04:50.185611Z INFO docmost_rust: --- startup ---
2026-04-12T00:04:52.918555Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
2026-04-12T00:04:53.326449Z INFO docmost_rust: spaces loaded: 6 spaces
2026-04-12T00:04:53.326521Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
2026-04-12T00:04:53.739110Z INFO docmost_rust: pages loaded: 1 pages
2026-04-12T00:04:54.599807Z INFO docmost_rust: pages loaded: 0 pages
2026-04-12T00:04:54.812194Z INFO docmost_rust: pages loaded: 8 pages
2026-04-12T00:04:55.018956Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T00:04:56.694690Z INFO docmost_rust::api: fetch_page_content: POST https://docmost.nakano47.com/api/pages/info page_id=019ac0d0-c5d5-7e66-ae14-3014e35fbb92
2026-04-12T00:04:56.955330Z INFO docmost_rust::api: fetch_page_content: status=200 OK body_len=2067
2026-04-12T00:04:56.955402Z 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![](/api/files/019ac112-ecf7-7208-8994-234185c895fb/image.png)\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-12T00:04:56.955798Z INFO docmost_rust::api: fetch_page_content: top-level keys = Some(["data", "status", "success"])
2026-04-12T00:04:56.955847Z INFO docmost_rust::api: fetch_page_content: extracted content len=1013
2026-04-12T00:04:57.065651Z INFO docmost_rust: page content loaded: id=019ac0d0-c5d5-7e66-ae14-3014e35fbb92 title="NextJS 16 midudev" content_len=1013
2026-04-12T00:04:57.065741Z 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-12T00:04:57.065848Z INFO docmost_rust: textarea lines: 41
2026-04-12T00:04:57.065912Z INFO docmost_rust: state → Editor
2026-04-12T00:05:58.064977Z INFO docmost_rust: --- startup ---
2026-04-12T00:06:00.362115Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
2026-04-12T00:06:00.700755Z INFO docmost_rust: spaces loaded: 6 spaces
2026-04-12T00:06:00.700822Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
2026-04-12T00:06:01.038449Z INFO docmost_rust: pages loaded: 1 pages
2026-04-12T00:06:49.046071Z INFO docmost_rust: pages loaded: 0 pages
2026-04-12T00:06:50.091604Z INFO docmost_rust: pages loaded: 1 pages
2026-04-12T00:07:01.651813Z INFO docmost_rust: --- startup ---
2026-04-12T00:07:13.506142Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
2026-04-12T00:07:13.863968Z INFO docmost_rust: spaces loaded: 6 spaces
2026-04-12T00:07:13.864003Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
2026-04-12T00:07:14.270595Z INFO docmost_rust: pages loaded: 1 pages
2026-04-12T00:07:14.638266Z INFO docmost_rust: pages loaded: 0 pages
2026-04-12T00:07:14.920261Z INFO docmost_rust: pages loaded: 8 pages
2026-04-12T00:07:15.126943Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T00:07:16.904905Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T00:07:19.368244Z INFO docmost_rust::api: fetch_page_content: POST https://docmost.nakano47.com/api/pages/info page_id=019d7ec0-317a-7431-bf97-ba733935b540
2026-04-12T00:07:19.647670Z INFO docmost_rust::api: fetch_page_content: status=200 OK body_len=1028
2026-04-12T00:07:19.647743Z INFO docmost_rust::api: fetch_page_content: raw body = {"data":{"id":"019d7ec0-317a-7431-bf97-ba733935b540","slugId":"JzF9KtdI1F","title":"page2","icon":null,"coverPhoto":null,"position":"a15Ae","parentPageId":null,"creatorId":"019a8369-7137-75b6-b171-cb7e11111fa7","lastUpdatedById":"019a8369-7137-75b6-b171-cb7e11111fa7","spaceId":"019d7eb5-f6cb-7083-bc21-b59526891088","workspaceId":"019a8369-713a-7aee-b3d9-45fb478c1f81","isLocked":false,"createdAt":"2026-04-11T22:53:38.040Z","updatedAt":"2026-04-11T22:53:41.191Z","deletedAt":null,"contributorIds":["019a8369-7137-75b6-b171-cb7e11111fa7"],"content":"asdf","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":"019d7eb5-f6cb-7083-bc21-b59526891088","name":"new workspace text","slug":"newworkspacetext"},"permissions":{"canEdit":true,"hasRestriction":false}},"success":true,"status":200}
2026-04-12T00:07:19.647957Z INFO docmost_rust::api: fetch_page_content: top-level keys = Some(["data", "status", "success"])
2026-04-12T00:07:19.648003Z INFO docmost_rust::api: fetch_page_content: extracted content len=4
2026-04-12T00:07:19.743748Z INFO docmost_rust: page content loaded: id=019d7ec0-317a-7431-bf97-ba733935b540 title="page2" content_len=4
2026-04-12T00:07:19.743832Z INFO docmost_rust: content preview: "asdf"
2026-04-12T00:07:19.743872Z INFO docmost_rust: textarea lines: 1
2026-04-12T00:07:19.743903Z INFO docmost_rust: state → Editor
2026-04-12T02:46:49.868189Z INFO docmost_rust: --- startup ---
2026-04-12T02:46:53.489346Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
2026-04-12T02:46:53.897690Z INFO docmost_rust: spaces loaded: 6 spaces
2026-04-12T02:46:53.897728Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
2026-04-12T02:46:54.303739Z INFO docmost_rust: pages loaded: 1 pages
2026-04-12T02:46:55.425332Z INFO docmost_rust: pages loaded: 0 pages
2026-04-12T02:46:56.224342Z INFO docmost_rust: pages loaded: 8 pages
2026-04-12T02:46:56.427450Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:46:57.071460Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:46:57.484168Z INFO docmost_rust: pages loaded: 4 pages
2026-04-12T02:46:58.314787Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:46:58.853440Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:46:59.451730Z INFO docmost_rust: pages loaded: 8 pages
2026-04-12T02:47:00.058586Z INFO docmost_rust: pages loaded: 0 pages
2026-04-12T02:47:00.825599Z INFO docmost_rust: pages loaded: 1 pages
2026-04-12T02:47:01.569118Z INFO docmost_rust: pages loaded: 0 pages
2026-04-12T02:47:02.354404Z INFO docmost_rust: pages loaded: 8 pages
2026-04-12T02:47:02.598611Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:47:02.836373Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:47:03.042656Z INFO docmost_rust: pages loaded: 4 pages
2026-04-12T02:47:25.793759Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:47:26.590697Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:47:27.226946Z INFO docmost_rust: pages loaded: 8 pages
2026-04-12T02:47:28.195328Z INFO docmost_rust: pages loaded: 0 pages
2026-04-12T02:47:29.044853Z INFO docmost_rust: pages loaded: 1 pages
2026-04-12T02:47:30.549784Z INFO docmost_rust: pages loaded: 0 pages
2026-04-12T02:47:30.752781Z INFO docmost_rust: pages loaded: 8 pages
2026-04-12T02:47:30.956119Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:53:54.487738Z INFO docmost_rust: --- startup ---
2026-04-12T02:53:57.411527Z INFO docmost_rust: login error: Email or password does not match
2026-04-12T02:54:01.511122Z INFO docmost_rust: login success, fetching spaces from https://docmost.nakano47.com/api
2026-04-12T02:54:01.927769Z INFO docmost_rust: spaces loaded: 6 spaces
2026-04-12T02:54:01.927806Z INFO docmost_rust: auto-loading pages for space 'Curso Cobol Platzi' (019b7051-6cde-7cfe-90cf-62787f33a2b0)
2026-04-12T02:54:02.333759Z INFO docmost_rust: pages loaded: 1 pages
2026-04-12T02:54:02.798463Z INFO docmost_rust: pages loaded: 0 pages
2026-04-12T02:54:03.697032Z INFO docmost_rust: pages loaded: 8 pages
2026-04-12T02:54:07.101138Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:54:07.871363Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:54:08.679531Z INFO docmost_rust: pages loaded: 4 pages
2026-04-12T02:54:09.360063Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:54:09.797205Z INFO docmost_rust: pages loaded: 2 pages
2026-04-12T02:54:09.797278Z INFO docmost_rust: pages loaded: 8 pages
2026-04-12T02:54:09.797297Z INFO docmost_rust: pages loaded: 0 pages
2026-04-12T02:54:09.797314Z INFO docmost_rust: pages loaded: 1 pages

View File

@@ -6,12 +6,14 @@ use serde::{Deserialize, Serialize};
pub struct Space { pub struct Space {
pub id: String, pub id: String,
pub name: String, pub name: String,
pub icon: Option<String>,
} }
#[derive(Debug, Clone, Deserialize)] #[derive(Debug, Clone, Deserialize)]
pub struct Page { pub struct Page {
pub id: String, pub id: String,
pub title: Option<String>, pub title: Option<String>,
pub icon: Option<String>,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@@ -252,6 +254,7 @@ pub struct SearchResultSpace {
pub struct SearchResult { pub struct SearchResult {
pub id: String, pub id: String,
pub title: Option<String>, pub title: Option<String>,
pub icon: Option<String>,
pub highlight: Option<String>, pub highlight: Option<String>,
pub space: Option<SearchResultSpace>, pub space: Option<SearchResultSpace>,
} }

View File

@@ -2,8 +2,12 @@ mod api;
mod app; mod app;
mod ui; mod ui;
use std::{io, sync::mpsc, time::{Duration, Instant}};
use std::fs::OpenOptions; use std::fs::OpenOptions;
use std::{
io,
sync::mpsc,
time::{Duration, Instant},
};
use crossterm::{ use crossterm::{
event::{self, Event, KeyCode, KeyModifiers}, event::{self, Event, KeyCode, KeyModifiers},
@@ -16,8 +20,14 @@ use tui_textarea::TextArea;
use tracing::info; use tracing::info;
use tracing_subscriber::fmt; use tracing_subscriber::fmt;
use api::{create_page, create_space, delete_page, delete_space, do_login, fetch_page_content, fetch_pages, fetch_spaces, save_page, search_pages}; use api::{
use app::{App, AppMsg, AppState, DeleteConfirmDialog, DeleteTarget, EditorFocus, EditorStatus, EditorView, LoginField, NewPageDialog, NewSpaceDialog, Panel, SearchView}; create_page, create_space, delete_page, delete_space, do_login, fetch_page_content,
fetch_pages, fetch_spaces, save_page, search_pages,
};
use app::{
App, AppMsg, AppState, DeleteConfirmDialog, DeleteTarget, EditorFocus, EditorStatus,
EditorView, LoginField, NewPageDialog, NewSpaceDialog, Panel, SearchView,
};
use ui::{draw_editor, draw_login, draw_main, draw_search}; use ui::{draw_editor, draw_login, draw_main, draw_search};
// ─── Entry point ────────────────────────────────────────────────────────────── // ─── Entry point ──────────────────────────────────────────────────────────────
@@ -96,7 +106,10 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
app.main.selected_space = 0; app.main.selected_space = 0;
app.main.pages = vec![]; app.main.pages = vec![];
if let Some(space) = app.main.spaces.first() { if let Some(space) = app.main.spaces.first() {
info!("auto-loading pages for space '{}' ({})", space.name, space.id); info!(
"auto-loading pages for space '{}' ({})",
space.name, space.id
);
app.main.loading_pages = true; app.main.loading_pages = true;
spawn_fetch_pages_msg(&app.base_url, &app.token, &space.id, &tx); spawn_fetch_pages_msg(&app.base_url, &app.token, &space.id, &tx);
} }
@@ -107,7 +120,11 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
app.main.pages = pages; app.main.pages = pages;
app.main.selected_page = 0; app.main.selected_page = 0;
} }
AppMsg::PageContentLoaded { page_id, title, content } => { AppMsg::PageContentLoaded {
page_id,
title,
content,
} => {
info!( info!(
"page content loaded: id={page_id} title={title:?} content_len={}", "page content loaded: id={page_id} title={title:?} content_len={}",
content.len() content.len()
@@ -155,7 +172,11 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
let tx2 = tx.clone(); let tx2 = tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
let msg = match fetch_page_content(&base_url, &token, &page_id).await { let msg = match fetch_page_content(&base_url, &token, &page_id).await {
Ok(content) => AppMsg::PageContentLoaded { page_id, title, content }, Ok(content) => AppMsg::PageContentLoaded {
page_id,
title,
content,
},
Err(e) => AppMsg::ApiError(e), Err(e) => AppMsg::ApiError(e),
}; };
let _ = tx2.send(msg); let _ = tx2.send(msg);
@@ -267,7 +288,7 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
AppState::Search => draw_search(f, &app.search), AppState::Search => draw_search(f, &app.search),
})?; })?;
if !event::poll(Duration::from_millis(50))? { if !event::poll(Duration::from_millis(200))? {
continue; continue;
} }
@@ -296,7 +317,10 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
let tx2 = tx.clone(); let tx2 = tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
let msg = match do_login(&url, &email, &password).await { let msg = match do_login(&url, &email, &password).await {
Ok(token) => AppMsg::LoginSuccess { token, base_url: url }, Ok(token) => AppMsg::LoginSuccess {
token,
base_url: url,
},
Err(e) => AppMsg::LoginError(e), Err(e) => AppMsg::LoginError(e),
}; };
let _ = tx2.send(msg); let _ = tx2.send(msg);
@@ -324,9 +348,13 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
// ── Delete confirm dialog intercepts all keys when open ── // ── Delete confirm dialog intercepts all keys when open ──
if let Some(dialog) = app.main.delete_dialog.as_mut() { if let Some(dialog) = app.main.delete_dialog.as_mut() {
if dialog.deleting { continue; } if dialog.deleting {
continue;
}
match key.code { match key.code {
KeyCode::Esc => { app.main.delete_dialog = None; } KeyCode::Esc => {
app.main.delete_dialog = None;
}
KeyCode::Char('d') => { KeyCode::Char('d') => {
dialog.deleting = true; dialog.deleting = true;
dialog.error = None; dialog.error = None;
@@ -359,10 +387,16 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
// ── New-space dialog intercepts all keys when open ── // ── New-space dialog intercepts all keys when open ──
if let Some(dialog) = app.main.new_space_dialog.as_mut() { if let Some(dialog) = app.main.new_space_dialog.as_mut() {
if dialog.creating { continue; } if dialog.creating {
continue;
}
match key.code { match key.code {
KeyCode::Esc => { app.main.new_space_dialog = None; } KeyCode::Esc => {
KeyCode::Backspace => { dialog.name.pop(); } app.main.new_space_dialog = None;
}
KeyCode::Backspace => {
dialog.name.pop();
}
KeyCode::Enter => { KeyCode::Enter => {
if dialog.name.trim().is_empty() { if dialog.name.trim().is_empty() {
dialog.error = Some("Name cannot be empty.".into()); dialog.error = Some("Name cannot be empty.".into());
@@ -374,7 +408,8 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
let name = dialog.name.trim().to_string(); let name = dialog.name.trim().to_string();
let tx2 = tx.clone(); let tx2 = tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
let msg = match create_space(&base_url, &token, &name).await { let msg = match create_space(&base_url, &token, &name).await
{
Ok(space) => AppMsg::SpaceCreated(space), Ok(space) => AppMsg::SpaceCreated(space),
Err(e) => AppMsg::CreateSpaceError(e), Err(e) => AppMsg::CreateSpaceError(e),
}; };
@@ -393,10 +428,16 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
// ── New-page dialog intercepts all keys when open ── // ── New-page dialog intercepts all keys when open ──
if let Some(dialog) = app.main.new_page_dialog.as_mut() { if let Some(dialog) = app.main.new_page_dialog.as_mut() {
if dialog.creating { continue; } if dialog.creating {
continue;
}
match key.code { match key.code {
KeyCode::Esc => { app.main.new_page_dialog = None; } KeyCode::Esc => {
KeyCode::Backspace => { dialog.title.pop(); } app.main.new_page_dialog = None;
}
KeyCode::Backspace => {
dialog.title.pop();
}
KeyCode::Enter => { KeyCode::Enter => {
if dialog.title.trim().is_empty() { if dialog.title.trim().is_empty() {
dialog.error = Some("Title cannot be empty.".into()); dialog.error = Some("Title cannot be empty.".into());
@@ -406,16 +447,23 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
let base_url = app.base_url.clone(); let base_url = app.base_url.clone();
let token = app.token.clone(); let token = app.token.clone();
let title = dialog.title.trim().to_string(); let title = dialog.title.trim().to_string();
let space_id = app.main.spaces let space_id = app
.main
.spaces
.get(app.main.selected_space) .get(app.main.selected_space)
.map(|s| s.id.clone()) .map(|s| s.id.clone())
.unwrap_or_default(); .unwrap_or_default();
let tx2 = tx.clone(); let tx2 = tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
let msg = match create_page(&base_url, &token, &space_id, &title).await { let msg =
Ok((page_id, title)) => AppMsg::PageCreated { page_id, title }, match create_page(&base_url, &token, &space_id, &title)
Err(e) => AppMsg::CreateError(e), .await
}; {
Ok((page_id, title)) => {
AppMsg::PageCreated { page_id, title }
}
Err(e) => AppMsg::CreateError(e),
};
let _ = tx2.send(msg); let _ = tx2.send(msg);
}); });
} }
@@ -469,15 +517,25 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
// d → delete focused item // d → delete focused item
KeyCode::Char('d') => { KeyCode::Char('d') => {
let target = match app.main.focus { let target = match app.main.focus {
Panel::Spaces => app.main.spaces Panel::Spaces => {
.get(app.main.selected_space) app.main.spaces.get(app.main.selected_space).map(|s| {
.map(|s| DeleteTarget::Space { id: s.id.clone(), name: s.name.clone() }), DeleteTarget::Space {
Panel::Pages => app.main.pages id: s.id.clone(),
.get(app.main.selected_page) name: s.name.clone(),
.map(|p| DeleteTarget::Page { }
id: p.id.clone(), })
title: p.title.clone().unwrap_or_else(|| "Untitled".into()), }
}), Panel::Pages => {
app.main.pages.get(app.main.selected_page).map(|p| {
DeleteTarget::Page {
id: p.id.clone(),
title: p
.title
.clone()
.unwrap_or_else(|| "Untitled".into()),
}
})
}
}; };
if let Some(target) = target { if let Some(target) = target {
app.main.delete_dialog = Some(DeleteConfirmDialog::new(target)); app.main.delete_dialog = Some(DeleteConfirmDialog::new(target));
@@ -511,13 +569,21 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
let base_url = app.base_url.clone(); let base_url = app.base_url.clone();
let token = app.token.clone(); let token = app.token.clone();
let page_id = page.id.clone(); let page_id = page.id.clone();
let title = page.title.clone().unwrap_or_else(|| "Untitled".into()); let title =
page.title.clone().unwrap_or_else(|| "Untitled".into());
let tx2 = tx.clone(); let tx2 = tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
let msg = match fetch_page_content(&base_url, &token, &page_id).await { let msg =
Ok(content) => AppMsg::PageContentLoaded { page_id, title, content }, match fetch_page_content(&base_url, &token, &page_id)
Err(e) => AppMsg::ApiError(e), .await
}; {
Ok(content) => AppMsg::PageContentLoaded {
page_id,
title,
content,
},
Err(e) => AppMsg::ApiError(e),
};
let _ = tx2.send(msg); let _ = tx2.send(msg);
}); });
} }
@@ -562,11 +628,18 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
let base_url = app.base_url.clone(); let base_url = app.base_url.clone();
let token = app.token.clone(); let token = app.token.clone();
let page_id = result.id.clone(); let page_id = result.id.clone();
let title = result.title.clone().unwrap_or_else(|| "Untitled".into()); let title =
result.title.clone().unwrap_or_else(|| "Untitled".into());
let tx2 = tx.clone(); let tx2 = tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
let msg = match fetch_page_content(&base_url, &token, &page_id).await { let msg = match fetch_page_content(&base_url, &token, &page_id)
Ok(content) => AppMsg::PageContentLoaded { page_id, title, content }, .await
{
Ok(content) => AppMsg::PageContentLoaded {
page_id,
title,
content,
},
Err(e) => AppMsg::ApiError(e), Err(e) => AppMsg::ApiError(e),
}; };
let _ = tx2.send(msg); let _ = tx2.send(msg);
@@ -610,7 +683,11 @@ async fn run_app(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::R
.unwrap_or_default(); .unwrap_or_default();
let tx2 = tx.clone(); let tx2 = tx.clone();
tokio::spawn(async move { tokio::spawn(async move {
let msg = match save_page(&base_url, &token, &page_id, &title, &content).await { let msg = match save_page(
&base_url, &token, &page_id, &title, &content,
)
.await
{
Ok(()) => AppMsg::PageSaved, Ok(()) => AppMsg::PageSaved,
Err(e) => AppMsg::SaveError(e), Err(e) => AppMsg::SaveError(e),
}; };

View File

@@ -158,7 +158,13 @@ pub fn draw_main(f: &mut Frame, main: &MainView) {
} else { } else {
main.spaces main.spaces
.iter() .iter()
.map(|s| ListItem::new(s.name.as_str())) .map(|s| {
let label = match &s.icon {
Some(icon) if !icon.is_empty() => format!("{} {}", icon, s.name),
_ => s.name.clone(),
};
ListItem::new(label)
})
.collect() .collect()
}; };
@@ -196,7 +202,14 @@ pub fn draw_main(f: &mut Frame, main: &MainView) {
} else { } else {
main.pages main.pages
.iter() .iter()
.map(|p| ListItem::new(p.title.as_deref().unwrap_or("Untitled"))) .map(|p| {
let title = p.title.as_deref().unwrap_or("Untitled");
let label = match &p.icon {
Some(icon) if !icon.is_empty() => format!("{} {}", icon, title),
_ => title.to_string(),
};
ListItem::new(label)
})
.collect() .collect()
}; };
@@ -605,9 +618,13 @@ pub fn draw_search(f: &mut Frame, search: &SearchView) {
.iter() .iter()
.map(|r| { .map(|r| {
let title = r.title.as_deref().unwrap_or("Untitled"); let title = r.title.as_deref().unwrap_or("Untitled");
let label = match &r.icon {
Some(icon) if !icon.is_empty() => format!("{} {}", icon, title),
_ => title.to_string(),
};
let space = r.space.as_ref().map(|s| s.name.as_str()).unwrap_or(""); let space = r.space.as_ref().map(|s| s.name.as_str()).unwrap_or("");
ListItem::new(Line::from(vec![ ListItem::new(Line::from(vec![
Span::raw(title), Span::raw(label),
Span::styled(format!(" [{space}]"), Style::default().fg(Color::DarkGray)), Span::styled(format!(" [{space}]"), Style::default().fg(Color::DarkGray)),
])) ]))
}) })
@@ -639,6 +656,10 @@ pub fn draw_search(f: &mut Frame, search: &SearchView) {
format!("Error: {err}") format!("Error: {err}")
} else if let Some(result) = search.results.get(search.selected) { } else if let Some(result) = search.results.get(search.selected) {
let title = result.title.as_deref().unwrap_or("Untitled"); let title = result.title.as_deref().unwrap_or("Untitled");
let title = match &result.icon {
Some(icon) if !icon.is_empty() => format!("{} {}", icon, title),
_ => title.to_string(),
};
let space = result let space = result
.space .space
.as_ref() .as_ref()