Compare commits
52 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ec7f1f8a14 | |||
| 24f6c797f2 | |||
| a2885a3283 | |||
| 0143f1e465 | |||
| dc57f9af20 | |||
| 984ede7d55 | |||
| fc398bd1f9 | |||
| 75faf3efaa | |||
| 4f5dd1f399 | |||
| 033ae23b7c | |||
| a0be70d5a6 | |||
| ef29427877 | |||
| 2af0155e6a | |||
| 8130c5cc68 | |||
| 67d23a229f | |||
| 6c9515c385 | |||
| d4f12f6773 | |||
| 65a70996c4 | |||
| 2b8443a1b1 | |||
| c169058915 | |||
| 4c2003d05e | |||
| 42bb758383 | |||
| 26dab9b26b | |||
| d1a4129323 | |||
| a283ddf457 | |||
| b172762288 | |||
| b3da7e84d8 | |||
| 96989be616 | |||
| 9514f15073 | |||
| 1a57eccd38 | |||
| fdfd82207d | |||
| 6e32acfeff | |||
| 999e9cbc36 | |||
| c71f9f3dc2 | |||
| d3e0351d05 | |||
| 4108ecdce9 | |||
| 4bf3e2a1df | |||
| e9da5eeba5 | |||
| d934f4ca89 | |||
| 5b54f9a7be | |||
| cac0276c51 | |||
| c5bd4c650d | |||
| 1b795fa971 | |||
| 4a8bd9a35b | |||
| 4a527dc70b | |||
| 97717beb6d | |||
| 872b693373 | |||
| dcecdc31b5 | |||
| 3212e3f754 | |||
| d6695de462 | |||
| c75efc3e7b | |||
| 58ad0de0db |
@@ -0,0 +1,2 @@
|
||||
[ 3482ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 3531ms] [LOG] [HMR] connected @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
@@ -0,0 +1,9 @@
|
||||
[ 210ms] [LOG] [Fast Refresh] rebuilding @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 1039ms] [LOG] [Fast Refresh] done in 930ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 1076ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 1086ms] [LOG] [HMR] connected @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 37298ms] [ERROR] Pattern attribute value [0-9() -]{10,16} is not a valid regular expression: Uncaught SyntaxError: Invalid regular expression: /[0-9() -]{10,16}/v: Invalid character in character class @ http://localhost:3000/motorista/entrar:0
|
||||
[ 60872ms] [LOG] [Fast Refresh] rebuilding @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 60976ms] [LOG] [Fast Refresh] done in 99ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 61021ms] [LOG] [Fast Refresh] done in 99ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 66490ms] [LOG] [Fast Refresh] done in 99ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
@@ -0,0 +1,13 @@
|
||||
[ 93ms] [LOG] [Fast Refresh] done in 99ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 953ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 963ms] [LOG] [HMR] connected @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 4345ms] [LOG] [Fast Refresh] rebuilding @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 4433ms] [LOG] [Fast Refresh] done in 189ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 4548ms] [VERBOSE] [DOM] Password forms should have (optionally hidden) username fields for accessibility: (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3000/entrar/senha:0
|
||||
[ 9731ms] [LOG] [Fast Refresh] done in 5486ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 9777ms] [LOG] [Fast Refresh] done in 5486ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 11070ms] [LOG] [Fast Refresh] done in 5486ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 30758ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=fBVPvwqt_6CU4TU3WWYmu' failed: @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 31764ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=fBVPvwqt_6CU4TU3WWYmu' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 32766ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=fBVPvwqt_6CU4TU3WWYmu' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 33776ms] [LOG] [HMR] connected @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
@@ -0,0 +1,79 @@
|
||||
[ 805ms] [INFO] %cDownload the React DevTools for a better development experience: https://react.dev/link/react-devtools font-weight:bold @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 813ms] [LOG] [HMR] connected @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 1121ms] [VERBOSE] [DOM] Password forms should have (optionally hidden) username fields for accessibility: (More info: https://goo.gl/9p2vKq) %o @ http://localhost:3000/entrar/senha:0
|
||||
[ 2267ms] [LOG] [Fast Refresh] rebuilding @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 2310ms] [LOG] [Fast Refresh] done in 144ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 9621ms] [LOG] [Fast Refresh] rebuilding @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 9763ms] [LOG] [Fast Refresh] done in 243ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 48693ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 48749ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 48826ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 51025ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 51539ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 51907ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 107213ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 118297ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 123464ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 125669ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 125771ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 126799ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 126941ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 127929ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 127981ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 154930ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 156620ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 157845ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 158106ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 158454ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 158504ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 158748ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 158848ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 158955ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 159307ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 159399ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 159488ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 159901ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 159993ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 187894ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 188000ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 205603ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 207208ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 207296ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 207951ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 208126ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 208244ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 208353ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 208415ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 208590ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 208695ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 208794ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 208878ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 208986ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 209132ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 209234ms] [LOG] [Fast Refresh] done in 39171ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 228278ms] [LOG] [Fast Refresh] rebuilding @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 228308ms] [LOG] [Fast Refresh] done in 3ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 238991ms] [LOG] [Fast Refresh] done in 10713ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 239037ms] [LOG] [Fast Refresh] done in 10713ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 239175ms] [LOG] [Fast Refresh] rebuilding @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 239206ms] [LOG] [Fast Refresh] done in 3ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 239602ms] [LOG] [Fast Refresh] done in 3ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 239697ms] [LOG] [Fast Refresh] done in 3ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 264111ms] [LOG] [Fast Refresh] done in 3ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 265916ms] [LOG] [Fast Refresh] done in 3ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 321743ms] [LOG] [Fast Refresh] done in 3ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 333352ms] [LOG] [Fast Refresh] done in 3ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 369410ms] [LOG] [Fast Refresh] done in 3ms @ http://localhost:3000/_next/static/chunks/1bba_next_dist_041c6vio7na_y._.js:719
|
||||
[ 413886ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 414902ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 415904ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 416906ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 417908ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 418911ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 423918ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 428928ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 433950ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 438984ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 444022ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 449184ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
[ 454479ms] [ERROR] WebSocket connection to 'ws://localhost:3000/_next/hmr?id=ruCdDO_vey1tElDs8WYR0' failed: Error in connection establishment: net::ERR_CONNECTION_REFUSED @ http://localhost:3000/_next/static/chunks/1bba_next_dist_client_0ru2jkh0js1s8._.js:16225
|
||||
@@ -0,0 +1,28 @@
|
||||
- generic [ref=e2]:
|
||||
- complementary [ref=e3]:
|
||||
- generic [ref=e8]:
|
||||
- generic [ref=e9]: Rota Fresca
|
||||
- generic [ref=e10]: Distribuição de alimentos
|
||||
- generic [ref=e11]:
|
||||
- heading "Do depósito à prateleira, na hora certa." [level=2] [ref=e12]
|
||||
- paragraph [ref=e13]: Pedidos, entregas e notas da sua rede no mesmo lugar.
|
||||
- main [ref=e14]:
|
||||
- link "Precisa de ajuda?" [ref=e16] [cursor=pointer]:
|
||||
- /url: mailto:suporte@rotafresca.com.br
|
||||
- generic [ref=e17]:
|
||||
- generic [ref=e18]:
|
||||
- heading "Entrar" [level=1] [ref=e19]
|
||||
- paragraph [ref=e20]: Use o e-mail da sua conta. Conforme o tipo de conta, pedimos a senha ou mandamos um link de acesso.
|
||||
- generic [ref=e21]:
|
||||
- generic [ref=e22]:
|
||||
- generic [ref=e23]: E-mail
|
||||
- textbox "E-mail" [ref=e24]:
|
||||
- /placeholder: nome@empresa.com.br
|
||||
- button "Continuar" [ref=e25]
|
||||
- generic [ref=e26]: ou
|
||||
- generic [ref=e33]:
|
||||
- paragraph [ref=e34]: É motorista?
|
||||
- paragraph [ref=e35]: Entre pelo app Rota Fresca no celular, com o seu número. Não precisa de e-mail.
|
||||
- generic [ref=e36]:
|
||||
- paragraph [ref=e37]: Recebeu um convite? Abra o link do e-mail para criar seu acesso.
|
||||
- paragraph [ref=e38]: Termos de uso · Privacidade
|
||||
@@ -0,0 +1,20 @@
|
||||
- generic [active] [ref=f1e1]:
|
||||
- generic [ref=f1e2]:
|
||||
- generic [ref=f1e3]:
|
||||
- generic [ref=f1e8]:
|
||||
- generic [ref=f1e9]: Rota Fresca
|
||||
- generic [ref=f1e10]: App do motorista
|
||||
- banner [ref=f1e11]:
|
||||
- heading "Entrar" [level=1] [ref=f1e12]
|
||||
- paragraph [ref=f1e13]: Use o número de celular que a central cadastrou. Mandamos um código, sem senha.
|
||||
- generic [ref=f1e14]:
|
||||
- generic [ref=f1e15]: Celular
|
||||
- generic [ref=f1e16]:
|
||||
- generic [ref=f1e17]: "+55"
|
||||
- textbox "(21) 98877-4410" [ref=f1e18]
|
||||
- generic [ref=f1e19]:
|
||||
- button "Receber código no WhatsApp" [ref=f1e20]
|
||||
- button "Receber por SMS" [ref=f1e21]
|
||||
- paragraph [ref=f1e22]: Trocou de número? Fale com a central.
|
||||
- button "Open Next.js Dev Tools" [ref=f1e28] [cursor=pointer]
|
||||
- alert [ref=f1e32]
|
||||
@@ -0,0 +1,32 @@
|
||||
- generic [active] [ref=f2e1]:
|
||||
- generic [ref=f2e2]:
|
||||
- complementary [ref=f2e3]:
|
||||
- generic [ref=f2e9]:
|
||||
- generic [ref=f2e10]: Rota Fresca
|
||||
- generic [ref=f2e11]: Distribuição de alimentos
|
||||
- generic [ref=f2e12]:
|
||||
- heading "Do depósito à prateleira, na hora certa." [level=2] [ref=f2e13]
|
||||
- paragraph [ref=f2e14]: Pedidos, entregas e notas da sua rede no mesmo lugar.
|
||||
- main [ref=f2e15]:
|
||||
- link "Precisa de ajuda?" [ref=f2e17] [cursor=pointer]:
|
||||
- /url: mailto:suporte@rotafresca.com.br
|
||||
- generic [ref=f2e18]:
|
||||
- generic [ref=f2e19]:
|
||||
- heading "Entrar" [level=1] [ref=f2e20]
|
||||
- paragraph [ref=f2e21]: Use o e-mail da sua conta. Conforme o tipo de conta, pedimos a senha ou mandamos um link de acesso.
|
||||
- generic [ref=f2e22]:
|
||||
- generic [ref=f2e23]:
|
||||
- generic [ref=f2e24]: E-mail
|
||||
- textbox "E-mail" [ref=f2e25]:
|
||||
- /placeholder: nome@empresa.com.br
|
||||
- button "Continuar" [ref=f2e26]
|
||||
- generic [ref=f2e27]: ou
|
||||
- button "Entrar com chave de acesso (passkey)" [ref=f2e31]
|
||||
- generic [ref=f2e35]:
|
||||
- paragraph [ref=f2e36]: É motorista?
|
||||
- paragraph [ref=f2e37]: Entre pelo app Rota Fresca no celular, com o seu número. Não precisa de e-mail.
|
||||
- generic [ref=f2e38]:
|
||||
- paragraph [ref=f2e39]: Recebeu um convite? Abra o link do e-mail para criar seu acesso.
|
||||
- paragraph [ref=f2e40]: Termos de uso · Privacidade
|
||||
- button "Open Next.js Dev Tools" [ref=f2e46] [cursor=pointer]
|
||||
- alert [ref=f2e50]
|
||||
@@ -0,0 +1,38 @@
|
||||
- generic [active] [ref=f2e1]:
|
||||
- button "Open Next.js Dev Tools" [ref=f2e46] [cursor=pointer]
|
||||
- alert [ref=f2e50]: Digite sua senha
|
||||
- generic [ref=f2e51]:
|
||||
- complementary [ref=f2e52]:
|
||||
- generic [ref=f2e58]:
|
||||
- generic [ref=f2e59]: Rota Fresca
|
||||
- generic [ref=f2e60]: Distribuição de alimentos
|
||||
- generic [ref=f2e61]:
|
||||
- heading "Do depósito à prateleira, na hora certa." [level=2] [ref=f2e62]
|
||||
- paragraph [ref=f2e63]: Pedidos, entregas e notas da sua rede no mesmo lugar.
|
||||
- main [ref=f2e64]:
|
||||
- link "Precisa de ajuda?" [ref=f2e66] [cursor=pointer]:
|
||||
- /url: mailto:suporte@rotafresca.com.br
|
||||
- generic [ref=f2e67]:
|
||||
- generic [ref=f2e68]:
|
||||
- generic [ref=f2e69]: ana.souza@rotafresca.com.br
|
||||
- button "Trocar" [ref=f2e74]
|
||||
- generic [ref=f2e75]:
|
||||
- heading "Digite sua senha" [level=1] [ref=f2e76]
|
||||
- paragraph [ref=f2e77]: Conta de consultor. Depois da senha pedimos o código do autenticador.
|
||||
- generic [ref=f2e78]:
|
||||
- generic [ref=f2e79]:
|
||||
- generic [ref=f2e80]:
|
||||
- generic [ref=f2e81]: Senha
|
||||
- textbox "Senha" [ref=f2e82]
|
||||
- generic [ref=f2e83]:
|
||||
- generic [ref=f2e84]:
|
||||
- checkbox "Mostrar senha" [ref=f2e85]
|
||||
- checkbox [aria-hidden]
|
||||
- text: Mostrar senha
|
||||
- link "Esqueci minha senha" [ref=f2e86] [cursor=pointer]:
|
||||
- /url: /entrar/redefinir
|
||||
- button "Entrar" [ref=f2e87]
|
||||
- paragraph [ref=f2e88]: Pode colar a senha ou usar o gerenciador de senhas do navegador.
|
||||
- generic [ref=f2e89]:
|
||||
- paragraph [ref=f2e90]: Recebeu um convite? Abra o link do e-mail para criar seu acesso.
|
||||
- paragraph [ref=f2e91]: Termos de uso · Privacidade
|
||||
@@ -0,0 +1,40 @@
|
||||
- generic [ref=f2e1]:
|
||||
- button "Open Next.js Dev Tools" [ref=f2e46] [cursor=pointer]
|
||||
- alert [ref=f2e50]: Rota Fresca
|
||||
- generic [ref=f2e51]:
|
||||
- complementary [ref=f2e52]:
|
||||
- generic [ref=f2e58]:
|
||||
- generic [ref=f2e59]: Rota Fresca
|
||||
- generic [ref=f2e60]: Distribuição de alimentos
|
||||
- generic [ref=f2e61]:
|
||||
- heading "Do depósito à prateleira, na hora certa." [level=2] [ref=f2e62]
|
||||
- paragraph [ref=f2e63]: Pedidos, entregas e notas da sua rede no mesmo lugar.
|
||||
- main [ref=f2e64]:
|
||||
- link "Precisa de ajuda?" [ref=f2e66] [cursor=pointer]:
|
||||
- /url: mailto:suporte@rotafresca.com.br
|
||||
- generic [ref=f2e67]:
|
||||
- generic [ref=f2e68]:
|
||||
- generic [ref=f2e69]: ana.souza@rotafresca.com.br
|
||||
- button "Trocar" [ref=f2e74]
|
||||
- generic [ref=f2e75]:
|
||||
- heading "Digite sua senha" [level=1] [ref=f2e76]
|
||||
- paragraph [ref=f2e77]: Conta de consultor. Depois da senha pedimos o código do autenticador.
|
||||
- generic [ref=f2e78]:
|
||||
- generic [ref=f2e79]:
|
||||
- generic [ref=f2e80]:
|
||||
- generic [ref=f2e81]: Senha
|
||||
- textbox "Senha" [active] [invalid] [ref=f2e82]
|
||||
- generic [ref=f2e83]:
|
||||
- generic [ref=f2e84]:
|
||||
- checkbox "Mostrar senha" [ref=f2e85]
|
||||
- checkbox [aria-hidden]
|
||||
- text: Mostrar senha
|
||||
- link "Esqueci minha senha" [ref=f2e86] [cursor=pointer]:
|
||||
- /url: /entrar/redefinir
|
||||
- alert [ref=f2e92]:
|
||||
- generic [ref=f2e93]: Senha incorreta. Confira e tente de novo.
|
||||
- button "Entrar" [ref=f2e87]
|
||||
- paragraph [ref=f2e88]: Pode colar a senha ou usar o gerenciador de senhas do navegador.
|
||||
- generic [ref=f2e89]:
|
||||
- paragraph [ref=f2e90]: Recebeu um convite? Abra o link do e-mail para criar seu acesso.
|
||||
- paragraph [ref=f2e91]: Termos de uso · Privacidade
|
||||
@@ -0,0 +1,32 @@
|
||||
- generic [active] [ref=f3e1]:
|
||||
- generic [ref=f3e2]:
|
||||
- complementary [ref=f3e3]:
|
||||
- generic [ref=f3e9]:
|
||||
- generic [ref=f3e10]: Rota Fresca
|
||||
- generic [ref=f3e11]: Distribuição de alimentos
|
||||
- generic [ref=f3e12]:
|
||||
- heading "Do depósito à prateleira, na hora certa." [level=2] [ref=f3e13]
|
||||
- paragraph [ref=f3e14]: Pedidos, entregas e notas da sua rede no mesmo lugar.
|
||||
- main [ref=f3e15]:
|
||||
- link "Precisa de ajuda?" [ref=f3e17] [cursor=pointer]:
|
||||
- /url: mailto:suporte@rotafresca.com.br
|
||||
- generic [ref=f3e18]:
|
||||
- generic [ref=f3e19]:
|
||||
- heading "Entrar" [level=1] [ref=f3e20]
|
||||
- paragraph [ref=f3e21]: Use o e-mail da sua conta. Conforme o tipo de conta, pedimos a senha ou mandamos um link de acesso.
|
||||
- generic [ref=f3e22]:
|
||||
- generic [ref=f3e23]:
|
||||
- generic [ref=f3e24]: E-mail
|
||||
- textbox "E-mail" [ref=f3e25]:
|
||||
- /placeholder: nome@empresa.com.br
|
||||
- button "Continuar" [ref=f3e26]
|
||||
- generic [ref=f3e27]: ou
|
||||
- button "Entrar com chave de acesso (passkey)" [ref=f3e31]
|
||||
- generic [ref=f3e35]:
|
||||
- paragraph [ref=f3e36]: É motorista?
|
||||
- paragraph [ref=f3e37]: Entre pelo app Rota Fresca no celular, com o seu número. Não precisa de e-mail.
|
||||
- generic [ref=f3e38]:
|
||||
- paragraph [ref=f3e39]: Recebeu um convite? Abra o link do e-mail para criar seu acesso.
|
||||
- paragraph [ref=f3e40]: Termos de uso · Privacidade
|
||||
- button "Open Next.js Dev Tools" [ref=f3e46] [cursor=pointer]
|
||||
- alert [ref=f3e50]
|
||||
@@ -0,0 +1,38 @@
|
||||
- generic [active] [ref=f3e1]:
|
||||
- button "Open Next.js Dev Tools" [ref=f3e46] [cursor=pointer]
|
||||
- alert [ref=f3e50]
|
||||
- generic [ref=f3e51]:
|
||||
- complementary [ref=f3e52]:
|
||||
- generic [ref=f3e58]:
|
||||
- generic [ref=f3e59]: Rota Fresca
|
||||
- generic [ref=f3e60]: Distribuição de alimentos
|
||||
- generic [ref=f3e61]:
|
||||
- heading "Do depósito à prateleira, na hora certa." [level=2] [ref=f3e62]
|
||||
- paragraph [ref=f3e63]: Pedidos, entregas e notas da sua rede no mesmo lugar.
|
||||
- main [ref=f3e64]:
|
||||
- link "Precisa de ajuda?" [ref=f3e66] [cursor=pointer]:
|
||||
- /url: mailto:suporte@rotafresca.com.br
|
||||
- generic [ref=f3e67]:
|
||||
- generic [ref=f3e68]:
|
||||
- generic [ref=f3e69]: ana.souza@rotafresca.com.br
|
||||
- button "Trocar" [ref=f3e74]
|
||||
- generic [ref=f3e75]:
|
||||
- heading "Digite sua senha" [level=1] [ref=f3e76]
|
||||
- paragraph [ref=f3e77]: Conta de consultor. Depois da senha pedimos o código do autenticador.
|
||||
- generic [ref=f3e78]:
|
||||
- generic [ref=f3e79]:
|
||||
- generic [ref=f3e80]:
|
||||
- generic [ref=f3e81]: Senha
|
||||
- textbox "Senha" [ref=f3e82]
|
||||
- generic [ref=f3e83]:
|
||||
- generic [ref=f3e84]:
|
||||
- checkbox "Mostrar senha" [ref=f3e85]
|
||||
- checkbox [aria-hidden]
|
||||
- text: Mostrar senha
|
||||
- link "Esqueci minha senha" [ref=f3e86] [cursor=pointer]:
|
||||
- /url: /entrar/redefinir
|
||||
- button "Entrar" [ref=f3e87]
|
||||
- paragraph [ref=f3e88]: Pode colar a senha ou usar o gerenciador de senhas do navegador.
|
||||
- generic [ref=f3e89]:
|
||||
- paragraph [ref=f3e90]: Recebeu um convite? Abra o link do e-mail para criar seu acesso.
|
||||
- paragraph [ref=f3e91]: Termos de uso · Privacidade
|
||||
@@ -0,0 +1,33 @@
|
||||
- generic [active] [ref=f3e1]:
|
||||
- button "Open Next.js Dev Tools" [ref=f3e46] [cursor=pointer]
|
||||
- alert [ref=f3e50]
|
||||
- generic [ref=f3e92]:
|
||||
- complementary [ref=f3e93]:
|
||||
- generic [ref=f3e99]:
|
||||
- generic [ref=f3e100]: Rota Fresca
|
||||
- generic [ref=f3e101]: Distribuição de alimentos
|
||||
- generic [ref=f3e102]:
|
||||
- heading "Do depósito à prateleira, na hora certa." [level=2] [ref=f3e103]
|
||||
- paragraph [ref=f3e104]: Pedidos, entregas e notas da sua rede no mesmo lugar.
|
||||
- main [ref=f3e105]:
|
||||
- link "Precisa de ajuda?" [ref=f3e107] [cursor=pointer]:
|
||||
- /url: mailto:suporte@rotafresca.com.br
|
||||
- generic [ref=f3e108]:
|
||||
- heading "Confirme que é você" [level=1] [ref=f3e113]
|
||||
- generic [ref=f3e114]:
|
||||
- generic [ref=f3e115]:
|
||||
- generic [ref=f3e116]:
|
||||
- generic [ref=f3e117]: Código
|
||||
- textbox "Código" [ref=f3e118]
|
||||
- generic [ref=f3e119]:
|
||||
- checkbox "Não pedir de novo neste computador por 30 dias" [checked] [ref=f3e120]
|
||||
- checkbox [checked] [aria-hidden]
|
||||
- text: Não pedir de novo neste computador por 30 dias
|
||||
- button "Confirmar" [ref=f3e121]
|
||||
- generic [ref=f3e122]:
|
||||
- link "Usar um código de recuperação" [ref=f3e123] [cursor=pointer]:
|
||||
- /url: /entrar/codigo?recuperacao=1
|
||||
- paragraph [ref=f3e124]: Perdeu o celular? Peça ao administrador da conta para refazer o autenticador.
|
||||
- generic [ref=f3e125]:
|
||||
- paragraph [ref=f3e126]: Recebeu um convite? Abra o link do e-mail para criar seu acesso.
|
||||
- paragraph [ref=f3e127]: Termos de uso · Privacidade
|
||||
@@ -0,0 +1,47 @@
|
||||
- generic [active] [ref=f3e1]:
|
||||
- button "Open Next.js Dev Tools" [ref=f3e46] [cursor=pointer]
|
||||
- alert [ref=f3e50]: Bom dia, Ana
|
||||
- generic [ref=f3e128]:
|
||||
- complementary [ref=f3e129]:
|
||||
- generic [ref=f3e130]:
|
||||
- generic [ref=f3e136]:
|
||||
- generic [ref=f3e137]: Rota Fresca
|
||||
- generic [ref=f3e138]: Distribuição de alimentos
|
||||
- navigation [ref=f3e139]:
|
||||
- link "Painel" [ref=f3e140] [cursor=pointer]:
|
||||
- /url: /painel
|
||||
- link "Roteiro do dia" [ref=f3e143] [cursor=pointer]:
|
||||
- /url: /roteiro
|
||||
- link "Lojas (PDVs)" [ref=f3e146] [cursor=pointer]:
|
||||
- /url: /lojas
|
||||
- link "Pedidos" [ref=f3e149] [cursor=pointer]:
|
||||
- /url: /pedidos
|
||||
- link "Entregas" [ref=f3e152] [cursor=pointer]:
|
||||
- /url: /entregas
|
||||
- link "Validade e lotes" [ref=f3e155] [cursor=pointer]:
|
||||
- /url: /validade
|
||||
- link "Relatórios" [ref=f3e158] [cursor=pointer]:
|
||||
- /url: /relatorios
|
||||
- link "Integrações e notas" [ref=f3e161] [cursor=pointer]:
|
||||
- /url: /integracoes
|
||||
- link "Leitura de documentos" [ref=f3e164] [cursor=pointer]:
|
||||
- /url: /documentos
|
||||
- link "Contatos e mensagens" [ref=f3e167] [cursor=pointer]:
|
||||
- /url: /contatos
|
||||
- link "Configurações" [ref=f3e170] [cursor=pointer]:
|
||||
- /url: /configuracoes
|
||||
- generic [ref=f3e173]:
|
||||
- generic [aria-hidden] [ref=f3e174]: A
|
||||
- generic [ref=f3e175]:
|
||||
- generic [ref=f3e176]: Ana Souza
|
||||
- generic [ref=f3e177]: Consultora · Zona Oeste
|
||||
- main [ref=f3e178]:
|
||||
- generic [ref=f3e179]:
|
||||
- generic [ref=f3e180]:
|
||||
- heading "Bom dia, Ana" [level=1] [ref=f3e181]
|
||||
- paragraph [ref=f3e182]: domingo, 25 de outubro
|
||||
- generic [ref=f3e183]:
|
||||
- textbox "Buscar loja, pedido ou produto" [ref=f3e187]
|
||||
- button "Notificações" [ref=f3e188]
|
||||
- button "Importar / Exportar" [ref=f3e189]
|
||||
- button "Novo pedido" [ref=f3e190]
|
||||
|
After Width: | Height: | Size: 344 KiB |
|
After Width: | Height: | Size: 70 KiB |
|
After Width: | Height: | Size: 33 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 344 KiB |
@@ -784,7 +784,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "craft-ui"
|
||||
version = "0.1.0"
|
||||
source = "git+https://git.marcospaulo.dev.br/masi/craft-libs?rev=22544b6d619d4f16779b8bdfa3f0b642a86fc065#22544b6d619d4f16779b8bdfa3f0b642a86fc065"
|
||||
source = "git+https://git.marcospaulo.dev.br/masi/craft-libs?rev=9c470aa935c4ce48f2ba4f0a17f9410c0c8660fb#9c470aa935c4ce48f2ba4f0a17f9410c0c8660fb"
|
||||
dependencies = [
|
||||
"egui",
|
||||
]
|
||||
@@ -3128,7 +3128,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "papercraft"
|
||||
version = "0.6.1"
|
||||
version = "0.8.2"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"axum",
|
||||
@@ -3161,6 +3161,7 @@ dependencies = [
|
||||
"resvg",
|
||||
"rfd",
|
||||
"rmcp",
|
||||
"ruzstd",
|
||||
"semver",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -3174,8 +3175,10 @@ dependencies = [
|
||||
"web-sys",
|
||||
"web-time",
|
||||
"wgpu",
|
||||
"windows-sys 0.61.2",
|
||||
"winit",
|
||||
"winresource",
|
||||
"zstd",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -3853,6 +3856,15 @@ version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||
|
||||
[[package]]
|
||||
name = "ruzstd"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7c1c839d570d835527c9a5e4db7cb2198683a988cb9d7293fc8674e6bd58fc8"
|
||||
dependencies = [
|
||||
"twox-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.23"
|
||||
@@ -4754,6 +4766,12 @@ dependencies = [
|
||||
"thiserror 2.0.21",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "twox-hash"
|
||||
version = "2.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "86a801b3cea342a06d468c8710662aa29e5e05e4f5c0d62f00bbb7f2ad7941c2"
|
||||
|
||||
[[package]]
|
||||
name = "type-map"
|
||||
version = "0.5.1"
|
||||
@@ -4777,7 +4795,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
|
||||
dependencies = [
|
||||
"memoffset",
|
||||
"tempfile",
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -6018,6 +6036,34 @@ version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b"
|
||||
|
||||
[[package]]
|
||||
name = "zstd"
|
||||
version = "0.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a"
|
||||
dependencies = [
|
||||
"zstd-safe",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-safe"
|
||||
version = "7.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64d80649ab6db9d9f6f9c80a40becd948eda4714a0a5ac8c4d157a32231c7882"
|
||||
dependencies = [
|
||||
"zstd-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zstd-sys"
|
||||
version = "2.1.2+zstd.1.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "481ada9c543b2323cbb7bbdbb5249177fbf0188ed959ed2c15e4b22421c84ed3"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.5.3"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "papercraft"
|
||||
version = "0.6.1"
|
||||
version = "0.8.2"
|
||||
edition = "2024"
|
||||
rust-version = "1.95"
|
||||
description = "A native infinite-canvas app for planning, working and sketching"
|
||||
@@ -44,7 +44,7 @@ env_logger = "0.11"
|
||||
rfd = "0.17"
|
||||
fastrand = "2"
|
||||
dirs = "6"
|
||||
craft-ui = { version = "0.1.0", git = "https://git.marcospaulo.dev.br/masi/craft-libs", rev = "22544b6d619d4f16779b8bdfa3f0b642a86fc065" }
|
||||
craft-ui = { version = "0.1.0", git = "https://git.marcospaulo.dev.br/masi/craft-libs", rev = "9c470aa935c4ce48f2ba4f0a17f9410c0c8660fb" }
|
||||
i_overlay = "9.0.1"
|
||||
miniz_oxide = "0.9.1"
|
||||
usvg = "0.48"
|
||||
@@ -97,8 +97,14 @@ reqwest = { version = "0.13", default-features = false, features = ["native-tls"
|
||||
# libc: SIGINT to the spawned chat CLIs.
|
||||
keyring = { version = "3", default-features = false, features = ["apple-native", "windows-native", "sync-secret-service", "crypto-rust"] }
|
||||
libc = "0.2"
|
||||
# Pages and versions on disk are zstd-compressed JSON (`project::packed`).
|
||||
zstd = "0.13"
|
||||
|
||||
# Browser build (`src/web.rs`): canvas + WebGPU/WebGL surface, DOM events instead of winit.
|
||||
# Process RAM and CPU readings for the performance overlay (`ui::perf`).
|
||||
[target.'cfg(windows)'.dependencies]
|
||||
windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_System_ProcessStatus", "Win32_System_Threading"] }
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
wgpu = { version = "30", features = ["webgl"] }
|
||||
wasm-bindgen = "0.2"
|
||||
@@ -106,4 +112,6 @@ wasm-bindgen-futures = "0.4"
|
||||
js-sys = "0.3"
|
||||
console_error_panic_hook = "0.1"
|
||||
console_log = "1"
|
||||
# zstd without C, for the browser: reads the packed files native builds write.
|
||||
ruzstd = "0.8"
|
||||
web-sys = { version = "0.3", features = ["Window", "Document", "Element", "HtmlElement", "HtmlCanvasElement", "KeyboardEvent", "PointerEvent", "WheelEvent", "ClipboardEvent", "DataTransfer", "MediaQueryList", "Location", "Performance", "Navigator", "Clipboard", "CssStyleDeclaration", "console", "WebSocket", "MessageEvent", "CloseEvent", "ErrorEvent", "Storage", "Crypto", "DragEvent", "FileList", "File", "Blob", "Request", "RequestInit", "Response"] }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Progress
|
||||
|
||||
Last updated: 2026-10-22.
|
||||
Last updated: 2026-10-24.
|
||||
|
||||
## Phase 3: local-first design and whiteboard tool
|
||||
|
||||
@@ -76,6 +76,125 @@ are credited in `THIRD_PARTY.md`.
|
||||
* **Not carried over**: comments, design tokens, AI image generation, video export, teams;
|
||||
moving layers between pages; remote image URLs (see README Known gaps).
|
||||
|
||||
### 2026-10-28: 0.8.2 (refresh-rate cap, accurate RAM/CPU, toolbar)
|
||||
|
||||
* Settings > Performance > Refresh rate is now a real frame cap applied live (30 to 360 Hz, 60 by default); **Auto** draws as fast as the display allows and is the default on macOS.
|
||||
* Performance Monitor: RAM is the private working set and CPU is divided by logical cores on Windows, so both match Task Manager.
|
||||
* Windows and Linux toolbar: a hamburger button shows or hides the File / Edit strip (remembered); the close button uses the theme's danger colour on the acrylic surface, with hover animation and a pointing-hand cursor.
|
||||
|
||||
### 2026-10-28: 0.8.1 (Performance Monitor on Windows, Design tab speed)
|
||||
|
||||
* Performance Monitor: RAM (working set) and CPU now show on Windows; before they were `n/a` there. The overlay and slow-frame badge judge frames against a refresh rate you pick in Settings > Performance: 30 to 360 Hz (144, 165 and 240 included), 60 by default.
|
||||
* Design tab: the lint and the open-feedback scan are kept until the document changes instead of walking every node each frame; a 12.6k-node page no longer drops to a few FPS while the tab is open.
|
||||
* Windows and Linux: window buttons in the toolbar; controls and acrylic come from craft-ui.
|
||||
|
||||
### 2026-10-27: 0.8.0 (screens, packed pages, canvas performance)
|
||||
|
||||
* Screens (`specs/screens`): a frame can be declared a screen or an overlay (drawer, modal, sheet) with `set_screens`, inspector toggles, or Declare N screens on the page. Agent edits to layers hidden behind an overlay or opaque layer in their screen are refused unless `allowHidden`; partly covered ones are reported with their visible share. `get_basic_info` lists screens, `get_node_info` tells a layer's visibility, `lint_design` skips hidden layers, On click lists screens, nested screens get canvas labels.
|
||||
* Pages and saved versions are stored as zstd-compressed compact JSON (`.json.zst`, project format 3): the 68 MB Interview project is 1.3 MB on disk and loads faster. A legacy `.json` still reads and is replaced on the next save; older builds refuse format 3.
|
||||
* Canvas performance: the editor logs which layers each change touched, so the scene cache re-tessellates only those and keeps no copy of the document (443 → 346 MB on Interview). Zoom holds its geometry until it settles, tiny layers are skipped, vertices are 24 bytes, redraws follow vsync, the idle pointer no longer redraws, freed memory goes back to the system when idle, off-screen frame labels are skipped, and the layers panel keeps its row heights between frames (panning a large page: ~9 → ~0.7 ms a frame).
|
||||
* Frame labels: none on frames too narrow on screen, overlapping labels are dropped (selected and larger frames first), long names end in an ellipsis.
|
||||
* Dropdowns size to their longest option (capped), keep the name of `A / B / Name` options, scroll when long and show the full name on hover.
|
||||
* Settings: a Performance page (FPS, frame time, RAM, CPU, GPU, optional overlay and slow-frame badge), agent logos in the agent rows; the Chat page no longer collapses.
|
||||
|
||||
### 2026-10-25: 0.8.0 (properties modal, row merges)
|
||||
|
||||
* Properties modal (inspector, on a main: Edit properties…): a tab per property (name, kind, layer, default, delete); an options property as a Table (a row per option, a column per layer · style; colours picked from the project's variables with a swatch, visibility as a toggle, icon, image and text fields; add or remove options and columns) or as Source (each option's changes as JSON with syntax highlighting); templates from the + Property menu; Save replaces them all. The inline list is name, kind and a remove button. The `status` template writes colours the project has as variables as those variables.
|
||||
* Canvas overlays (agent glow, agent outlines, review outlines, the agent-elsewhere chip) sit below modals instead of showing through them. Two segmented controls in one row no longer share widget ids (the second did not respond).
|
||||
* `merge_variants` keeps what each moved instance shows (texts that came from its old variant), carries nested instances' property values (a badge's Tone) in the options, and takes `textLayers` for state texts only.
|
||||
|
||||
### 2026-10-25: 0.8.0 (prototype links tool, merge icon colours)
|
||||
|
||||
* `set_prototype_links`: agents can link a layer to a screen for Present mode (back arrows, rows, tabs); before, only the inspector could.
|
||||
* `merge_variants` compares an icon's colour (keeping its variable), now that icons are compared as one unit.
|
||||
|
||||
### 2026-10-25: 0.8.0 (data safety fixes)
|
||||
|
||||
* Sync no longer throws away an instance written over a nested one the way older versions stored it (its own root, no swap): it becomes a swap of the main's child at that place. Projects built before #11 lost such layers (a checked checkbox, a status badge) on the next edit of the outer main.
|
||||
* Icons: the HTML parser keeps attribute names in lowercase, so the icon and plugin code never removed the SVG's original `viewBox`; since #14 that made `<x-paper-icon>` (and icon swaps) scale twice and come out tiny. Unreleased builds only; 0.7.3 is not affected.
|
||||
* `merge_variants` refuses merges that would leave one option and changes nothing then.
|
||||
|
||||
### 2026-10-25: 0.8.0 (merge variants, pinned bars)
|
||||
|
||||
* `merge_variants`: per-state components (`Tab bar / Hoje`, `Status / Sucesso`...) become one component with an enum property. Each variant's differences from the base, matched by layer path (fills, colours, borders, weight, radius, visibility, icons; texts on request), become its option; every instance on every page moves to the base with its value; the base takes the family name; the emptied variants are deleted. A variant with layers the base lacks is left out and reported.
|
||||
* Absolute children of a scrolling screen stay pinned while its content scrolls (a tab bar, a floating button), and do not add to the scroll range.
|
||||
|
||||
### 2026-10-25: 0.8.0 (fix #22)
|
||||
|
||||
* #22: a hug column whose width comes from its parent (stretched or filled) re-measures its height once its text wraps at that width, and the parent measures again, instead of keeping a height measured while the text was narrow.
|
||||
|
||||
### 2026-10-25: 0.8.0 (component properties)
|
||||
|
||||
* Main components carry properties (`Node.props`): `text`, `bool`, `icon` and `enum`, whose options are changes in the overrides format (a layer name, `$root` or a path `Item > Label`, to a text or styles that may hold `text`, `icon` and `visibility`), so they restyle, retext, show, hide or re-icon any layer. Instances keep their values (`prop_values`) as overrides, so main edits keep them; changing the definitions re-applies every instance on every page.
|
||||
* Templates: `text` (a property per text layer), `active` (which item of a tab bar, nav or segmented row is active) and `status` (options with a colour tint the root and colour the label and icon).
|
||||
* `set_component_props`, `set_instance_props`, `props='{...}'` on `<x-paper-instance>`, properties in `get_components`, `{Prop}` in code link templates, and an inspector section (a control per property on an instance; Texts and Active item templates on a main). Icon swaps keep the layer's name and link (a `children` override); recoloured icon shapes inside instances now keep their colour after a sync.
|
||||
* Spec: `specs/component-props/spec.md`.
|
||||
|
||||
### 2026-10-25: 0.8.0 (scrolling screens)
|
||||
|
||||
* Screens keep their size and scroll what overflows: a frame's `scroll` flag (CSS `overflow: auto|scroll`, `overflow-y-auto` in the Tailwind export) makes it fixed height and clipped, and the wheel over it scrolls its content on the canvas and in Present, then pans once the content ends. The offset is view state, not saved; hit-testing and overlays follow it.
|
||||
* Toggles: a scroll button beside each screen's name (shown when it scrolls, overflows, or is hovered), Scroll overflow on a selected frame, and Screens scroll on the page for every screen at once. A fit-content screen that grew goes back to its min height.
|
||||
* Agents are told to keep screens at their size and use overflow:auto for long content.
|
||||
|
||||
### 2026-10-25: 0.8.0 (fixes #19, #20, #21)
|
||||
|
||||
* #19: a clipping frame draws its border after its children, so filled rows no longer hide it. Clipping stays rectangular: children's square corners still show past a rounded border.
|
||||
* #20: a border written with a colour variable (`1px solid var(--border)`) binds the stroke, a literal one unbinds it, and reads show `var(--name)` for bound strokes. `find_nodes` says when it stopped at 500 matches.
|
||||
* #21: hug (fit-content) frames grow around growing children (CSS flex-basis auto) instead of staying at their min height; with short content the min height stays and the growing child fills it, so a pinned footer works.
|
||||
|
||||
### 2026-10-25: 0.8.0 (follow agents, skeletons)
|
||||
|
||||
* Follow agents (Settings, Chat; on by default): in Design and Chat mode alike, the canvas pans, and switches page, to the layer any agent changes (the chat, Claude Code over MCP, a helper). Moving the canvas yourself pauses it for a moment; off, a chip offers the agent's page.
|
||||
* Skeletons: the moment the model starts writing a tool call (`ToolDraft` and `ToolInput` events from Claude Code's stream and the API), the canvas shows a shimmering skeleton over its target, or where a new artboard goes, labelled with what it is doing, until the call has run. Fresh agent content comes in through a skeleton that fades, so an outline never frames empty space.
|
||||
* Top bar: the title and the agent pill leave room measured from the buttons on the right, so nothing overlaps at narrow widths.
|
||||
|
||||
### 2026-10-25: 0.8.0 (fixes #14 to #18)
|
||||
|
||||
* #14: `write_html` fits an `<svg>` viewBox into its width and height like a browser (centred, aspect kept).
|
||||
* #15: a note on something placed straight in its artboard treats the artboard as the screen: tidy keeps cards off it instead of in its free space.
|
||||
* #16: `not-an-instance` no longer matches look-alikes with a different icon; its fix names the `<x-paper-instance>` to write (swap_instance refuses plain frames).
|
||||
* #17: instances with their own text re-measure it when the main's text style changes, so hug buttons keep fitting their labels (also covers the size reset after a swap).
|
||||
* #18: `lint_design` reports borders whose hex equals a color variable, and `fix` binds them (`stroke`), so a mode switch reaches them.
|
||||
|
||||
### 2026-10-25: 0.7.3 (QA sub-agent, helper agents, auto mode, mid-turn messages, isolated dev settings)
|
||||
|
||||
* QA sub-agent: Claude Code chats always get a Sonnet `qa` agent that lints and screenshots built frames and returns only findings, so screenshots stay out of the main context.
|
||||
* Helper agents (`src/mcp/delegate.rs`, Settings, Chat, Helper): `delegate` hands `qa`, `read` or `build` tasks to a cheaper agent CLI (pi with MiniMax M3.1 Flash, opencode, an Ollama-backed agent, any command taking `{prompt}`). It drives PaperCraft through `papercraft-cli`, runs off the UI thread, and only its last 6,000 characters come back. With a helper set, the chat agent orchestrates: keeps the brief and taste, delegates review, bulk reading and fully specified edits.
|
||||
* Dev config isolation: `PAPERCRAFT_HOME` also isolates the chat settings (`$PAPERCRAFT_HOME/.config`), so a second copy for testing never touches the installed app's settings.
|
||||
* Live check in an isolated copy: `delegate` ran the pi helper (MiniMax M3.1 Flash) as QA, a message added mid-turn went Waiting then Seen, and the agent drafted #13.
|
||||
* Fixes #10, #11, #12, #13:
|
||||
* #10: `move_nodes` to another page keeps mains' component keys and relinks every instance to moved nodes; new `rename_file` renames a project.
|
||||
* #11: swapped nested instances persist as swap overrides (`InstanceLink.swap`), preserving component identity across outer component updates and copies.
|
||||
* #12: `move_nodes` to another page takes the moved nodes' annotations along; detached notes resolve as `Detached #n`.
|
||||
* #13: `get_computed_styles` reports a text colour bound to a variable as `var(--name)`.
|
||||
* Auto mode (the composer's Auto chip): when a turn ends on `ask_user` questions, the first (recommended) option of each is sent and the chat goes on, until Stop or a turn hits its budget. The agent is told to put its recommendation first, default what only the user can know, and end each turn with next steps.
|
||||
* The Present button uses a play icon drawn like the icons beside it. Composer chips wrap instead of running under Stop and Add. Top bar: the agent pill drops its text when it would run under the buttons on the right.
|
||||
* While a chat turn works, the composer's Add (or Enter) queues a message (`src/mcp/steer.rs`). It rides on the agent's next PaperCraft tool result for that chat, so every backend reads it mid-turn; the composer shows each as Waiting or Seen. What no tool call picked up is sent as the next message when the turn ends, or goes back into the box after Stop.
|
||||
|
||||
### 2026-10-24: 0.7.2 (UI kits, code links, issues from the app, design playbook)
|
||||
|
||||
* Code links (Figma Code Connect style): a main component carries its library import and a React and an egui template (`set_code_link`). The React, Tailwind, inline JSX and egui exports write linked instances as that code, with overrides filled in and imports gathered once; the AI spec lists them as Library Components with a "use it, do not rebuild it" rule and stops describing their inner layers.
|
||||
* UI kits (`src/ui_kits.rs`, `apply_ui_kit`): shadcn/ui, Ant Design, React Bits and egui, each with Light and Dark variables, tokens and components in its look and density, all linked. The chat brief asks for the code library and starts from its kit.
|
||||
* Issues from the app (`src/issues.rs`): the agent's `report_issue` draft card (Send or Discard) and a Report a PaperCraft issue form, filed on the repository in Settings (token in the keychain, else the `tea` login), de-duplicated by title against open and closed issues.
|
||||
* design-playbook in the chat: with the `playbook` CLI installed, the brief asks whether to follow the playbook and the agent looks rules up with `design_rules`, citing ids in its report.
|
||||
* Top bar: project name and page picker on one centred row; the two-row title had outgrown the bar and covered the canvas.
|
||||
* Evidence: `src/ui_kits.rs`, `src/issues.rs`, `src/mcp/codegen.rs` (`linked_components_export_as_library_code`), `specs/ui-kits/spec.md`.
|
||||
|
||||
### 2026-10-24: 0.7.1 (rename fix)
|
||||
|
||||
* Enter, Escape and clicking away end a rename again (projects on the home screen, library assets, versions). The fields took focus back before checking whether they had lost it.
|
||||
|
||||
### 2026-10-24: 0.7.0 (images, research, plugins, fewer tokens)
|
||||
|
||||
* Images in chat: paste from the clipboard (Cmd/Ctrl+V with an image-only clipboard now reaches the composer), thumbnails in the composer and in sent messages, a lightbox, image-only messages. Attachments are sent at most 1568 px (originals kept); `zoom_image` reads a region of the original.
|
||||
* Brief and finish: `ask_user` draws questions as a card of clickable answers (palette swatches, reference image tiles, the recommended answer marked). The chat rules ask for a brief before the first design, variables and components first, sizes from the agreed density, a short report and next steps as a card.
|
||||
* Web research (the composer's Web chip): `fetch_reference_images` (public addresses only, icons, logos and backdrops dropped, social cards last), a References gallery that dims what the agent did not keep; on Claude Code a Haiku `researcher` sub-agent does the searching.
|
||||
* Image generation, opt-in (Settings, Chat, Images): Codex (offered once when signed in with a ChatGPT plan), Google Flow through `gf`, or any command (`{prompt}`, `{aspect}`, `{ratio}`, `{out}`, passed as data).
|
||||
* Plugins (Settings, Plugins): Avataaars, DiceBear, Iconify, Lorem Picsum, Flags, QR codes, placed with `<x-plugin use="…">`, cached on disk. SVG import draws masked groups as images (Avataaars).
|
||||
* Tokens: lean Claude Code sessions (42.7k to 20.6k prompt tokens), deferred tool schemas and context editing on the API, a fresh session with a recap past 120k (or on demand from the meter), per-turn cost for resumed Claude Code sessions, a 1M context meter, a turn budget. Model and effort pickers for Claude Code.
|
||||
* Also: page switcher in the Chat top bar, steps stay folded while a turn runs (no flicker), `lint_design` asks 24 px targets on desktop screens, the installer keeps `papercraft-mcp` current.
|
||||
* Evidence: `src/mcp/{refs,imagegen}.rs`, `src/plugins.rs`, `src/io/svg_import.rs`, `src/ui/{chat,chat_msgs,chat_agents,settings}.rs`, `src/chat/{anthropic,claude_code}.rs`, `tests/chat_tools.rs`, `specs/chat-tokens/spec.md`.
|
||||
|
||||
### 2026-10-22: 0.6.1 (agents via ACP, annotations)
|
||||
|
||||
* Agents are found through the login shell's PATH (read once at startup, 3 s limit, merged with the app PATH and the usual folders), so an app opened from the Dock finds Claude Code, Codex and the rest. No install instructions anywhere; a missing agent shows Not found with Set path… and Docs.
|
||||
|
||||
@@ -90,7 +90,7 @@ Projects live in `~/Documents/papercraft/<name>/` (`project.json`, one JSON file
|
||||
|
||||
## Agents
|
||||
|
||||
PaperCraft exposes **67 tools** (`src/mcp/tools.rs`) three ways. They run the same code inside the app.
|
||||
PaperCraft exposes **70 tools** (`src/mcp/tools.rs`) three ways. They run the same code inside the app.
|
||||
|
||||
| Way | Notes |
|
||||
|---|---|
|
||||
|
||||
@@ -62,3 +62,15 @@ SOFTWARE.
|
||||
|
||||
Inter 4.1 by Rasmus Andersson — SIL Open Font License 1.1 (`assets/fonts/OFL.txt`). Regular,
|
||||
Medium, SemiBold, Bold, Italic and Bold Italic are bundled unmodified.
|
||||
|
||||
Interface fonts, SIL Open Font License 1.1, Latin subsets as served by Google Fonts, unmodified
|
||||
(Regular, Medium, SemiBold): Geist (Vercel), Figtree (Erik Kennedy), DM Sans (Colophon Foundry,
|
||||
Jonny Pinhorn), Nunito (Vernon Adams, Jacques Le Bailly), Quicksand (Andrew Paglinawan),
|
||||
Fredoka (Milena Brandão, Hafontia), and Varela Round (Joe Prince; Regular only).
|
||||
|
||||
## Agent logos
|
||||
|
||||
`assets/agents/*.svg` are from LobeHub Icons (`@lobehub/icons-static-svg` 1.80.0,
|
||||
https://github.com/lobehub/lobe-icons), MIT License, Copyright (c) LobeHub. They are used
|
||||
unmodified to identify each coding agent in the agent picker. The marks are trademarks of
|
||||
their owners.
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Claude</title><path d="M4.709 15.955l4.72-2.647.08-.23-.08-.128H9.2l-.79-.048-2.698-.073-2.339-.097-2.266-.122-.571-.121L0 11.784l.055-.352.48-.321.686.06 1.52.103 2.278.158 1.652.097 2.449.255h.389l.055-.157-.134-.098-.103-.097-2.358-1.596-2.552-1.688-1.336-.972-.724-.491-.364-.462-.158-1.008.656-.722.881.06.225.061.893.686 1.908 1.476 2.491 1.833.365.304.145-.103.019-.073-.164-.274-1.355-2.446-1.446-2.49-.644-1.032-.17-.619a2.97 2.97 0 01-.104-.729L6.283.134 6.696 0l.996.134.42.364.62 1.414 1.002 2.229 1.555 3.03.456.898.243.832.091.255h.158V9.01l.128-1.706.237-2.095.23-2.695.08-.76.376-.91.747-.492.584.28.48.685-.067.444-.286 1.851-.559 2.903-.364 1.942h.212l.243-.242.985-1.306 1.652-2.064.73-.82.85-.904.547-.431h1.033l.76 1.129-.34 1.166-1.064 1.347-.881 1.142-1.264 1.7-.79 1.36.073.11.188-.02 2.856-.606 1.543-.28 1.841-.315.833.388.091.395-.328.807-1.969.486-2.309.462-3.439.813-.042.03.049.061 1.549.146.662.036h1.622l3.02.225.79.522.474.638-.079.485-1.215.62-1.64-.389-3.829-.91-1.312-.329h-.182v.11l1.093 1.068 2.006 1.81 2.509 2.33.127.578-.322.455-.34-.049-2.205-1.657-.851-.747-1.926-1.62h-.128v.17l.444.649 2.345 3.521.122 1.08-.17.353-.608.213-.668-.122-1.374-1.925-1.415-2.167-1.143-1.943-.14.08-.674 7.254-.316.37-.729.28-.607-.461-.322-.747.322-1.476.389-1.924.315-1.53.286-1.9.17-.632-.012-.042-.14.018-1.434 1.967-2.18 2.945-1.726 1.845-.414.164-.717-.37.067-.662.401-.589 2.388-3.036 1.44-1.882.93-1.086-.006-.158h-.055L4.132 18.56l-1.13.146-.487-.456.061-.746.231-.243 1.908-1.312-.006.006z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Cline</title><path d="M17.035 3.991c2.75 0 4.98 2.24 4.98 5.003v1.667l1.45 2.896a1.01 1.01 0 01-.002.909l-1.448 2.864v1.668c0 2.762-2.23 5.002-4.98 5.002H7.074c-2.751 0-4.98-2.24-4.98-5.002V17.33l-1.48-2.855a1.01 1.01 0 01-.003-.927l1.482-2.887V8.994c0-2.763 2.23-5.003 4.98-5.003h9.962zM8.265 9.6a2.274 2.274 0 00-2.274 2.274v4.042a2.274 2.274 0 004.547 0v-4.042A2.274 2.274 0 008.265 9.6zm7.326 0a2.274 2.274 0 00-2.274 2.274v4.042a2.274 2.274 0 104.548 0v-4.042A2.274 2.274 0 0015.59 9.6z"></path><path d="M12.054 5.558a2.779 2.779 0 100-5.558 2.779 2.779 0 000 5.558z"></path></svg>
|
||||
|
After Width: | Height: | Size: 750 B |
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Cursor</title><path d="M22.106 5.68L12.5.135a.998.998 0 00-.998 0L1.893 5.68a.84.84 0 00-.419.726v11.186c0 .3.16.577.42.727l9.607 5.547a.999.999 0 00.998 0l9.608-5.547a.84.84 0 00.42-.727V6.407a.84.84 0 00-.42-.726zm-.603 1.176L12.228 22.92c-.063.108-.228.064-.228-.061V12.34a.59.59 0 00-.295-.51l-9.11-5.26c-.107-.062-.063-.228.062-.228h18.55c.264 0 .428.286.296.514z"></path></svg>
|
||||
|
After Width: | Height: | Size: 547 B |
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Gemini</title><path d="M20.616 10.835a14.147 14.147 0 01-4.45-3.001 14.111 14.111 0 01-3.678-6.452.503.503 0 00-.975 0 14.134 14.134 0 01-3.679 6.452 14.155 14.155 0 01-4.45 3.001c-.65.28-1.318.505-2.002.678a.502.502 0 000 .975c.684.172 1.35.397 2.002.677a14.147 14.147 0 014.45 3.001 14.112 14.112 0 013.679 6.453.502.502 0 00.975 0c.172-.685.397-1.351.677-2.003a14.145 14.145 0 013.001-4.45 14.113 14.113 0 016.453-3.678.503.503 0 000-.975 13.245 13.245 0 01-2.003-.678z"></path></svg>
|
||||
|
After Width: | Height: | Size: 651 B |
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>GithubCopilot</title><path d="M19.245 5.364c1.322 1.36 1.877 3.216 2.11 5.817.622 0 1.2.135 1.592.654l.73.964c.21.278.323.61.323.955v2.62c0 .339-.173.669-.453.868C20.239 19.602 16.157 21.5 12 21.5c-4.6 0-9.205-2.583-11.547-4.258-.28-.2-.452-.53-.453-.868v-2.62c0-.345.113-.679.321-.956l.73-.963c.392-.517.974-.654 1.593-.654l.029-.297c.25-2.446.81-4.213 2.082-5.52 2.461-2.54 5.71-2.851 7.146-2.864h.198c1.436.013 4.685.323 7.146 2.864zm-7.244 4.328c-.284 0-.613.016-.962.05-.123.447-.305.85-.57 1.108-1.05 1.023-2.316 1.18-2.994 1.18-.638 0-1.306-.13-1.851-.464-.516.165-1.012.403-1.044.996a65.882 65.882 0 00-.063 2.884l-.002.48c-.002.563-.005 1.126-.013 1.69.002.326.204.63.51.765 2.482 1.102 4.83 1.657 6.99 1.657 2.156 0 4.504-.555 6.985-1.657a.854.854 0 00.51-.766c.03-1.682.006-3.372-.076-5.053-.031-.596-.528-.83-1.046-.996-.546.333-1.212.464-1.85.464-.677 0-1.942-.157-2.993-1.18-.266-.258-.447-.661-.57-1.108-.32-.032-.64-.049-.96-.05zm-2.525 4.013c.539 0 .976.426.976.95v1.753c0 .525-.437.95-.976.95a.964.964 0 01-.976-.95v-1.752c0-.525.437-.951.976-.951zm5 0c.539 0 .976.426.976.95v1.753c0 .525-.437.95-.976.95a.964.964 0 01-.976-.95v-1.752c0-.525.437-.951.976-.951zM7.635 5.087c-1.05.102-1.935.438-2.385.906-.975 1.037-.765 3.668-.21 4.224.405.394 1.17.657 1.995.657h.09c.649-.013 1.785-.176 2.73-1.11.435-.41.705-1.433.675-2.47-.03-.834-.27-1.52-.63-1.813-.39-.336-1.275-.482-2.265-.394zm6.465.394c-.36.292-.6.98-.63 1.813-.03 1.037.24 2.06.675 2.47.968.957 2.136 1.104 2.776 1.11h.044c.825 0 1.59-.263 1.995-.657.555-.556.765-3.187-.21-4.224-.45-.468-1.335-.804-2.385-.906-.99-.088-1.875.058-2.265.394zM12 7.615c-.24 0-.525.015-.84.044.03.16.045.336.06.526l-.001.159a2.94 2.94 0 01-.014.25c.225-.022.425-.027.612-.028h.366c.187 0 .387.006.612.028-.015-.146-.015-.277-.015-.409.015-.19.03-.365.06-.526a9.29 9.29 0 00-.84-.044z"></path></svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Goose</title><path d="M21.595 23.61c1.167-.254 2.405-.944 2.405-.944l-2.167-1.784a12.124 12.124 0 01-2.695-3.131 12.127 12.127 0 00-3.97-4.049l-.794-.462a1.115 1.115 0 01-.488-.815.844.844 0 01.154-.575c.413-.582 2.548-3.115 2.94-3.44.503-.416 1.065-.762 1.586-1.159.074-.056.148-.112.221-.17.003-.002.007-.004.009-.007.167-.131.325-.272.45-.438.453-.524.563-.988.59-1.193-.061-.197-.244-.639-.753-1.148.319.02.705.272 1.056.569.235-.376.481-.773.727-1.171.165-.266-.08-.465-.086-.471h-.001V3.22c-.007-.007-.206-.25-.471-.086-.567.35-1.134.702-1.639 1.021 0 0-.597-.012-1.305.599a2.464 2.464 0 00-.438.45l-.007.009c-.058.072-.114.147-.17.221-.397.521-.743 1.083-1.16 1.587-.323.391-2.857 2.526-3.44 2.94a.842.842 0 01-.574.153 1.115 1.115 0 01-.815-.488l-.462-.794a12.123 12.123 0 00-4.049-3.97 12.133 12.133 0 01-3.13-2.695L1.332 0S.643 1.238.39 2.405c.352.428 1.27 1.49 2.34 2.302C1.58 4.167.73 3.75.06 3.4c-.103.765-.063 1.92.043 2.816.726.317 1.961.806 3.219 1.066-1.006.236-2.11.278-2.961.262.15.554.358 1.119.64 1.688.119.263.25.52.39.77.452.125 2.222.383 3.164.171l-2.51.897a27.776 27.776 0 002.544 2.726c2.031-1.092 2.494-1.241 4.018-2.238-2.467 2.008-3.108 2.828-3.8 3.67l-.483.678c-.25.351-.469.725-.65 1.117-.61 1.31-1.47 4.1-1.47 4.1-.154.486.202.842.674.674 0 0 2.79-.861 4.1-1.47.392-.182.766-.4 1.118-.65l.677-.483c.227-.187.453-.37.701-.586 0 0 1.705 2.02 3.458 3.349l.896-2.511c-.211.942.046 2.712.17 3.163.252.142.509.272.772.392.569.28 1.134.49 1.688.64-.016-.853.026-1.956.261-2.962.26 1.258.75 2.493 1.067 3.219.895.106 2.051.146 2.816.043a73.87 73.87 0 01-1.308-2.67c.811 1.07 1.874 1.988 2.302 2.34h-.001z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.8 KiB |
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Kimi</title><path d="M21.846 0a1.923 1.923 0 110 3.846H20.15a.226.226 0 01-.227-.226V1.923C19.923.861 20.784 0 21.846 0z"></path><path d="M11.065 11.199l7.257-7.2c.137-.136.06-.41-.116-.41H14.3a.164.164 0 00-.117.051l-7.82 7.756c-.122.12-.302.013-.302-.179V3.82c0-.127-.083-.23-.185-.23H3.186c-.103 0-.186.103-.186.23V19.77c0 .128.083.23.186.23h2.69c.103 0 .186-.102.186-.23v-3.25c0-.069.025-.135.069-.178l2.424-2.406a.158.158 0 01.205-.023l6.484 4.772a7.677 7.677 0 003.453 1.283c.108.012.2-.095.2-.23v-3.06c0-.117-.07-.212-.164-.227a5.028 5.028 0 01-2.027-.807l-5.613-4.064c-.117-.078-.132-.279-.028-.381z"></path></svg>
|
||||
|
After Width: | Height: | Size: 786 B |
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Mistral</title><path clip-rule="evenodd" d="M3.428 3.4h3.429v3.428h3.429v3.429h-.002 3.431V6.828h3.427V3.4h3.43v13.714H24v3.429H13.714v-3.428h-3.428v-3.429h-3.43v3.428h3.43v3.429H0v-3.429h3.428V3.4zm10.286 13.715h3.428v-3.429h-3.427v3.429z"></path></svg>
|
||||
|
After Width: | Height: | Size: 418 B |
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>OpenAI</title><path d="M9.205 8.658v-2.26c0-.19.072-.333.238-.428l4.543-2.616c.619-.357 1.356-.523 2.117-.523 2.854 0 4.662 2.212 4.662 4.566 0 .167 0 .357-.024.547l-4.71-2.759a.797.797 0 00-.856 0l-5.97 3.473zm10.609 8.8V12.06c0-.333-.143-.57-.429-.737l-5.97-3.473 1.95-1.118a.433.433 0 01.476 0l4.543 2.617c1.309.76 2.189 2.378 2.189 3.948 0 1.808-1.07 3.473-2.76 4.163zM7.802 12.703l-1.95-1.142c-.167-.095-.239-.238-.239-.428V5.899c0-2.545 1.95-4.472 4.591-4.472 1 0 1.927.333 2.712.928L8.23 5.067c-.285.166-.428.404-.428.737v6.898zM12 15.128l-2.795-1.57v-3.33L12 8.658l2.795 1.57v3.33L12 15.128zm1.796 7.23c-1 0-1.927-.332-2.712-.927l4.686-2.712c.285-.166.428-.404.428-.737v-6.898l1.974 1.142c.167.095.238.238.238.428v5.233c0 2.545-1.974 4.472-4.614 4.472zm-5.637-5.303l-4.544-2.617c-1.308-.761-2.188-2.378-2.188-3.948A4.482 4.482 0 014.21 6.327v5.423c0 .333.143.571.428.738l5.947 3.449-1.95 1.118a.432.432 0 01-.476 0zm-.262 3.9c-2.688 0-4.662-2.021-4.662-4.519 0-.19.024-.38.047-.57l4.686 2.71c.286.167.571.167.856 0l5.97-3.448v2.26c0 .19-.07.333-.237.428l-4.543 2.616c-.619.357-1.356.523-2.117.523zm5.899 2.83a5.947 5.947 0 005.827-4.756C22.287 18.339 24 15.84 24 13.296c0-1.665-.713-3.282-1.998-4.448.119-.5.19-.999.19-1.498 0-3.401-2.759-5.947-5.946-5.947-.642 0-1.26.095-1.88.31A5.962 5.962 0 0010.205 0a5.947 5.947 0 00-5.827 4.757C1.713 5.447 0 7.945 0 10.49c0 1.666.713 3.283 1.998 4.448-.119.5-.19 1-.19 1.499 0 3.401 2.759 5.946 5.946 5.946.642 0 1.26-.095 1.88-.309a5.96 5.96 0 004.162 1.713z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Qwen</title><path d="M12.604 1.34c.393.69.784 1.382 1.174 2.075a.18.18 0 00.157.091h5.552c.174 0 .322.11.446.327l1.454 2.57c.19.337.24.478.024.837-.26.43-.513.864-.76 1.3l-.367.658c-.106.196-.223.28-.04.512l2.652 4.637c.172.301.111.494-.043.77-.437.785-.882 1.564-1.335 2.34-.159.272-.352.375-.68.37-.777-.016-1.552-.01-2.327.016a.099.099 0 00-.081.05 575.097 575.097 0 01-2.705 4.74c-.169.293-.38.363-.725.364-.997.003-2.002.004-3.017.002a.537.537 0 01-.465-.271l-1.335-2.323a.09.09 0 00-.083-.049H4.982c-.285.03-.553-.001-.805-.092l-1.603-2.77a.543.543 0 01-.002-.54l1.207-2.12a.198.198 0 000-.197 550.951 550.951 0 01-1.875-3.272l-.79-1.395c-.16-.31-.173-.496.095-.965.465-.813.927-1.625 1.387-2.436.132-.234.304-.334.584-.335a338.3 338.3 0 012.589-.001.124.124 0 00.107-.063l2.806-4.895a.488.488 0 01.422-.246c.524-.001 1.053 0 1.583-.006L11.704 1c.341-.003.724.032.9.34zm-3.432.403a.06.06 0 00-.052.03L6.254 6.788a.157.157 0 01-.135.078H3.253c-.056 0-.07.025-.041.074l5.81 10.156c.025.042.013.062-.034.063l-2.795.015a.218.218 0 00-.2.116l-1.32 2.31c-.044.078-.021.118.068.118l5.716.008c.046 0 .08.02.104.061l1.403 2.454c.046.081.092.082.139 0l5.006-8.76.783-1.382a.055.055 0 01.096 0l1.424 2.53a.122.122 0 00.107.062l2.763-.02a.04.04 0 00.035-.02.041.041 0 000-.04l-2.9-5.086a.108.108 0 010-.113l.293-.507 1.12-1.977c.024-.041.012-.062-.035-.062H9.2c-.059 0-.073-.026-.043-.077l1.434-2.505a.107.107 0 000-.114L9.225 1.774a.06.06 0 00-.053-.031zm6.29 8.02c.046 0 .058.02.034.06l-.832 1.465-2.613 4.585a.056.056 0 01-.05.029.058.058 0 01-.05-.029L8.498 9.841c-.02-.034-.01-.052.028-.054l.216-.012 6.722-.012z"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
@@ -0,0 +1 @@
|
||||
<svg fill="currentColor" fill-rule="evenodd" height="1em" style="flex:none;line-height:1" viewBox="0 0 24 24" width="1em" xmlns="http://www.w3.org/2000/svg"><title>Tencent</title><path d="M9.976 1L24 9.8l-10.587.015L10.723 23H5.489L8.18 9.8H3.244L1 5.4h8.077L9.976 1z"></path></svg>
|
||||
|
After Width: | Height: | Size: 282 B |
@@ -6,7 +6,7 @@ FROM rust:slim AS build
|
||||
# Must match the wasm-bindgen crate in Cargo.lock.
|
||||
ARG WASM_BINDGEN=0.2.129
|
||||
RUN rustup target add wasm32-unknown-unknown \
|
||||
&& apt-get update && apt-get install -y --no-install-recommends binaryen pkg-config libssl-dev \
|
||||
&& apt-get update && apt-get install -y --no-install-recommends binaryen pkg-config libssl-dev libdbus-1-dev \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& cargo install wasm-bindgen-cli --version ${WASM_BINDGEN} --locked
|
||||
COPY --from=vendor crates /vendor
|
||||
@@ -21,7 +21,7 @@ COPY . .
|
||||
RUN sh web/build.sh && cargo build --release --locked --bin papercraft-relay
|
||||
|
||||
FROM debian:stable-slim
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends libssl3 ca-certificates \
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends libssl3 libdbus-1-3 ca-certificates \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& useradd --system --uid 10001 papercraft
|
||||
COPY --from=build /src/target/release/papercraft-relay /usr/local/bin/papercraft-relay
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# Driving PaperCraft with agents
|
||||
|
||||
PaperCraft exposes one set of 67 tools three ways. They all run the same code inside the app, so an agent sees what you see and every call is one undo step.
|
||||
PaperCraft exposes one set of 70 tools three ways. They all run the same code inside the app, so an agent sees what you see and every call is one undo step.
|
||||
|
||||
| Way | Use it when | Needs the app running |
|
||||
|---|---|---|
|
||||
| MCP over HTTP, `http://127.0.0.1:29980/mcp` | the agent client speaks MCP (Claude Code, Codex, pi) | yes |
|
||||
| MCP over stdio, `papercraft-mcp` | the client only starts stdio servers | for calls; `tools/list` works offline |
|
||||
| Shell, `papercraft-cli` | the agent has a terminal and you want no tool schemas in its context | for `call`; `list`, `schema` and `icons` work offline |
|
||||
| Shell, `papercraft-cli` | the agent has a terminal and you want no tool schemas in its context | for `call`; `list`, `schema` and `icons` work offline (with the app running, `list` and `schema` show the app's tools) |
|
||||
|
||||
If you would rather talk to the agent inside the app, Chat mode (⌘J, or `Design | Chat` in the top bar) is the in-app alternative. It runs Claude Code or Codex through their CLIs against the same MCP server, ACP agents such as OpenCode or Gemini CLI, or the Anthropic API with your own key, and keeps the conversation next to the canvas, with the same runs, review and revert. The setup and the thread are in [chat.md](chat.md). The rest of this guide covers driving the app from your own terminal or agent client.
|
||||
|
||||
@@ -91,6 +91,8 @@ papercraft-cli call get_screenshot '{"nodeId":"abc123:1"}' --project Demo -o car
|
||||
|
||||
### Shell and scripts
|
||||
|
||||
When `call` or `shell` reaches the app and the app is another version, the CLI prints once on stderr, for example `papercraft-cli 0.6.1 ≠ app 0.6.2; reinstall the CLI`. `list` and `schema` read the running app's tool list, so tools newer than the CLI still show; with the app closed they use the CLI's built-in list.
|
||||
|
||||
`papercraft-cli shell [--project X] [--offline]` keeps one session open (offline, one in-process project; otherwise the app). A line is `<tool> <json>` (JSON may span lines until its braces close) or a command: `:help :tools :schema <tool> :open <project> :pages :page <name|id> :shot [nodeId] [file] :undo :redo :save :watch on|off :quit`. `:watch on` renders the artboard last edited to `preview.png` after each editing call, shown inline on iTerm2, WezTerm and kitty. Input is plain stdin (no line editing or history; use `rlwrap` for that). Piped input prints no prompt and exits 1 if any line failed, listing the line numbers on stderr.
|
||||
|
||||
```sh
|
||||
@@ -112,11 +114,19 @@ EOF
|
||||
* Components: `get_components` lists mains of every page and enabled library with a `componentKey`; `create_instance` and `swap_instance` accept a main from any page or library (`componentId` or `componentKey`).
|
||||
* Libraries: `publish_library`, `list_libraries`, `enable_library`, `update_library`, `disable_library`. They work offline too, on the project store.
|
||||
|
||||
## Seeing what the user sees
|
||||
|
||||
* `get_basic_info` and `get_selection` include `viewport: {pageId, rect: [x, y, w, h], zoom}`: the page the user is viewing and the world rect on screen.
|
||||
* `set_viewport {pageId?, nodeIds? | rect?, zoom?, padding?, force?}` switches the user's page (as the page list does) and fits the nodes or rect, or centres them at an explicit `zoom` (0.01 to 256; `select_nodes {zoom: true}` stops at 100%). While the user is panning or zooming (10 s after their last camera move) it is refused with "user is moving the canvas", unless `force: true`.
|
||||
* `find_nodes {textValue}` finds text layers containing the phrase on every page (or `pageId`), ignoring case and line breaks; `*` wildcards match the whole value instead. `get_tree_summary {nodeId, fullText: true}` prints every text layer's complete text, for copy review across screens.
|
||||
* `get_screenshot {nodeId, annotations: true}` draws the notes on the captured layers (cards, number badges, leader lines), so an agent can check note placement.
|
||||
|
||||
## Annotation tools
|
||||
|
||||
* `annotate {pageId?, notes: [{node, text, type?}]}` adds many notes in one undo step. `node` is a node id or an exact layer name; the first line of `text` is the label and the rest the body; `type` is a category key (default `rule`) that the page's template must enable. Placement is automatic.
|
||||
* `annotate {pageId?, notes: [{node, text, type?}]}` adds many notes in one undo step. `node` is a node id or an exact layer name; the first line of `text` is the label and the rest the body; `type` is a category key (default `rule`) that the page's template must enable. Placement is automatic. `annotate {tidy: true, frame?}` with no notes re-lays-out the existing notes and returns `{"tidied": n}`, the number of notes it moved.
|
||||
* `get_annotation_template {pageId?}` returns the allowed types with their guidance and fields. Call it once before `annotate`.
|
||||
* `set_annotation_template {template, scope?: "page" | "project", pageId?}` switches the template by id or name.
|
||||
* `upsert_annotation_template {template: {id?, name, display?, categories: [{key, enabled?, guidance?, fields?}]}, categories?: [{key, name, color, icon}], setAs?: "project" | "page", pageId?}` creates or replaces a template and its category styles in one undo step. `delete_annotation_template {template}` removes one (the last stays).
|
||||
* `get_annotations {nodeId?, pageId?, full?}` reads notes as `[number, node, type, text]` rows.
|
||||
* `update_annotation {id | ref, text?, type?}` and `delete_annotations {ids}` take ids or references such as `Sign in #3`.
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ The Annotate popover offers only the enabled categories of the active template,
|
||||
|
||||
Three levels, all view state. They are not saved in the design and not undoable.
|
||||
|
||||
* **Full**: every note shows its label and the first lines of its body.
|
||||
* **Full**: every note shows its label and the first lines of its body. Below 60% zoom notes show as badges, so cards never spill over the design; zoom in to read them.
|
||||
* **Badges**: only the number badges are shown.
|
||||
* **Hidden**: nothing is drawn.
|
||||
|
||||
@@ -98,11 +98,27 @@ Agents work with notes through six MCP tools, all in `src/mcp/tools.rs`. They ar
|
||||
|---|---|
|
||||
| `get_annotation_template` | Reads the template: `[type key, guidance, field names]` for each enabled type. Call it once before `annotate`. |
|
||||
| `set_annotation_template` | Switches the template by id or name, for one page (default) or the project (`scope: "project"`). |
|
||||
| `upsert_annotation_template` | Creates or replaces a template, and creates or restyles categories in the same call (`categories: [{key, name, color, icon}]`). `setAs: "project"` or `"page"` makes it the default or the page override. Returns `{id, name, types}`. |
|
||||
| `delete_annotation_template` | Deletes a template by id or name. The last template stays. |
|
||||
| `annotate` | Adds many notes at once. Each note names a node id or an exact layer name on the page, a `text` and a `type` (default `rule`). |
|
||||
| `get_annotations` | Reads notes as `[number, nodeName, type, text]` rows (text capped at 120 characters). `full: true` returns complete objects; `nodeId` limits to a subtree; `pageId` picks another page. |
|
||||
| `update_annotation` | Changes the text or type of one note, by `id` or by reference such as `Sign in #3`. |
|
||||
| `delete_annotations` | Deletes notes by ids or references such as `Sign in #3`. |
|
||||
|
||||
Custom template with a new category, used on the current page:
|
||||
|
||||
```json
|
||||
{
|
||||
"template": {"name": "Legal review", "categories": [
|
||||
{"key": "legal", "guidance": "Legal risk this part carries.", "fields": ["Owner", "Law"]},
|
||||
{"key": "rule"}]},
|
||||
"categories": [{"key": "legal", "name": "Legal", "color": "#aa3355", "icon": "scale"}],
|
||||
"setAs": "page"
|
||||
}
|
||||
```
|
||||
|
||||
`icon` must be one of the built-in icon names (an unknown name lists them). Every key in the template must exist, either already or in `categories`. The template and category edit is one undo step; `setAs: "page"` adds a second.
|
||||
|
||||
For `annotate` and `update_annotation`, the first line of `text` is the label and the rest is the body. There are no positions, icons or colours: PaperCraft places each note beside its target, outside the frame, stacked so none overlap. `annotate` is all or nothing: if one note is refused (an unknown layer, or a type the template disables), none is added and the error lists the allowed keys.
|
||||
|
||||
Batched call, on a page whose template is Product spec:
|
||||
@@ -119,6 +135,10 @@ Batched call, on a page whose template is Product spec:
|
||||
|
||||
The reply is one row per note, `[number, node, type, label]`. Then `get_annotations {}` reads them back, and `delete_annotations {"ids":["Sign in #3"]}` removes one by its frame reference.
|
||||
|
||||
`annotate {"tidy": true}` with no notes re-runs placement for every note on the page (or on `frame`) in one undo step and returns `{"tidied": n}`, the number of notes it moved; `0` means the layout was already tidy.
|
||||
|
||||
To see the notes, `get_screenshot {"nodeId": "<frame>", "annotations": true}` draws the cards, number badges and leader lines of the notes whose target is inside the captured layers, placed and routed as on the canvas (full cards, scale 1). The image grows to include the cards. Without `annotations`, or with `false`, the screenshot is the same as before.
|
||||
|
||||
**Handoff.** `get_design_spec` and the code exports (`get_jsx`) include the notes of the frame as comments. Each note is one line, `// 1 Business rule: Lock after 5 failed tries`, with its body lines as more `//` lines, so code generated from a frame carries the rules next to the markup.
|
||||
|
||||
## Storage
|
||||
|
||||
@@ -96,9 +96,48 @@ Replies are markdown: paragraphs, lists, code blocks with a copy button, inline
|
||||
* **Plan**: when the agent keeps a todo list (Claude Code's `TodoWrite`, or an ACP plan), the latest list is pinned above the composer with a "done of total" count.
|
||||
* **Approval**: on the API backend and for ACP agents, the destructive tools (`delete_pages`, `disable_library`, `import_design_system`, and `delete_nodes` on more than 20 layers) wait for Approve or Deny before they run. Deny returns an error to the model, which can then carry on. For ACP agents, any non-PaperCraft tool the agent asks for shows the same card. The Claude Code and Codex CLIs do not show this card. They use the CLI's own permission rules, and in non-interactive mode a tool that needs permission is denied. The papercraft tools are pre-allowed on both CLIs.
|
||||
* **Checkpoint**: when an agent run starts, the thread shows "Checkpoint · before run N". "Restore to here" reverts that run and every run after it, through the same revert as the Agent view.
|
||||
* **Run card**: when a run ends, "Agent finished, N changes" with Review, Revert and Accept. These are the same actions as the run review on the canvas.
|
||||
* **Run card**: when a run ends, "Agent finished, N changes" with Review, Revert and Accept. These are the same actions as the run review on the canvas. The card names the client that made the run ("Chat", "Claude Code · 2") and refreshes after an undo.
|
||||
* **Progress**: while a run is open, the step group and the Working pill count calls and touched layers ("Styling · 212 calls · 1,340 changes"). The pill clears when the turn ends.
|
||||
* **Errors**: a failed turn shows a red row with Retry. A sign-in problem shows the sign-in card (see Setup).
|
||||
* **Canvas follow**: when a tool call targets a node, the canvas pans to it, unless you moved the camera in the last 10 seconds.
|
||||
* **Agent presence**: after each agent call, the layers it pointed at or changed get an accent outline that fades over 3 seconds, and the newest carries a label with the verb and the client ("Styling · Claude Code"). This works for chat turns and for any MCP client. When the agent works on another page, a chip "Agent on page X →" switches to it.
|
||||
|
||||
## Design-system context
|
||||
|
||||
Every turn carries a summary of the project's design system, so the agent reuses what exists instead of drawing look-alikes:
|
||||
|
||||
* variables by collection and group with their modes (colors as `name=#light/#dark`), tokens as scales;
|
||||
* main components on every page and in enabled libraries, as `componentKey · name · w×h · uses`, most used first;
|
||||
* enabled libraries and their update status, and the page's annotation template;
|
||||
* a reuse rule: place components with `create_instance` / `swap_instance`, colors as `var(--…)`, spacing on tokens.
|
||||
|
||||
The summary is capped at about 1,500 tokens. A large system keeps its most used components and collapses variable groups to counts, with a pointer to `get_components` / `get_variables` for the rest. It is rebuilt only when the design system changes. The API backends get it as a separate system block (cached on `api.anthropic.com`); CLI and ACP agents get it in their first prompt, and again as `[Design system changed] …` on the next message after a change. The context meter includes it. Turn it off in Settings → Chat → **Send design-system summary**.
|
||||
|
||||
`lint_design` has a matching rule, `not-an-instance`: a frame built by hand that matches a component (same structure, size within 4 px, same fill, radius and stroke) is flagged with the component to use.
|
||||
|
||||
## Pointing at the canvas
|
||||
|
||||
* **Pick**: the Pick chip next to `@ Selection` turns the canvas into a picker without changing your selection. Click a layer to add it, or Shift+drag an area; Esc cancels. Each pick types `@Name` at the composer cursor and adds a chip.
|
||||
* **⌥-click**: in Chat mode, ⌥-click a layer on the canvas to add it the same way.
|
||||
* **Area chips** are sent as `[Area] page P, rect x,y,w,h, contains: <ids…>` (the top layers wholly inside the area, up to 40).
|
||||
* **Crops**: layer, area and `@ Selection` chips carry an image of that part of the page with its notes drawn (1024 px max). The API backend gets it as an image block; CLI and ACP agents get a file under `.papercraft-chat-tmp/`.
|
||||
* **Ask (⌘K)**: with a selection, ⌘K opens a prompt box anchored above it. Enter sends to the chat with the selection chip and its crop, and the reply streams in the chat column without leaving Design mode. With nothing selected, ⌘K opens the command palette (also ⌘⇧/).
|
||||
|
||||
## Actions on a selection
|
||||
|
||||
The **Actions** menu opens from the canvas right-click menu, from the Actions chip in the composer and from the command palette ("Actions: …"). It works in Design and Chat modes.
|
||||
|
||||
Instant actions run in the app with no model. Each one is a single undo step and its own run (client "Actions"), and a toast reports what it did with Review and Revert:
|
||||
|
||||
* **Fix lint here**: `lint_design {fix: true}` on the selection.
|
||||
* **Bind to tokens**: binds hard-coded colors to the nearest variable (within 12 RGB) and gaps, padding and radii to the nearest spacing or radius token (within 2 px). Values with no close match are listed.
|
||||
* **Use components**: replaces each `not-an-instance` hit with an instance of its component in the same place, keeping the texts.
|
||||
* **Rename layers**: names default layers (`Frame 12`, `Text`) from their text or role.
|
||||
* **Annotate**: opens the annotation popover.
|
||||
|
||||
Agent actions send a prompt with a selection chip to the chat and switch to Chat mode: **Optimize** (tighten layout and spacing to the token scale, fix contrast, keep the content, prefer components), **Make consistent with the design system**, **Write states** (empty, loading, error), **Variations ×3** and **Annotate with AI**.
|
||||
|
||||
**Send open notes to chat**, in the inspector's Annotations section for a top-level frame, sends the frame's open feedback and its `todo` / `question` notes as one prompt with a chip.
|
||||
|
||||
## Composer and attachments
|
||||
|
||||
@@ -110,7 +149,7 @@ Type in the composer, ⏎ sends and ⇧⏎ adds a line. The empty state offers s
|
||||
|
||||
For the API backend, images are sent as image blocks, so the model sees them. For the CLIs and ACP agents, each image is saved under `.papercraft-chat-tmp/` in the project and its path is added to the prompt for the agent to read.
|
||||
|
||||
While a turn runs, the send button becomes **Stop**. Stop asks the backend to interrupt: the CLIs get an interrupt, or SIGINT, then a kill after 3 seconds; ACP agents get `session/cancel`, then a kill if they do not exit. Every backend ends within 3 seconds. Tools that already ran stay applied, and the partial run can be reviewed or reverted like any other.
|
||||
While a turn runs, the send button becomes **Stop**. Stop asks the backend to interrupt: the CLIs get an interrupt, or SIGINT, then a kill after 3 seconds; ACP agents get `session/cancel`, then a kill if they do not exit. Every backend ends within 3 seconds. The thread shows a muted "Stopped" line, not an error. Tools that already ran stay applied, and the partial run can be reviewed or reverted like any other.
|
||||
|
||||
## Storage
|
||||
|
||||
@@ -128,7 +167,9 @@ Prompts, project content and tool results go to whichever backend you chose: the
|
||||
|
||||
* One turn per project at a time. Sending while a turn runs is refused with "A turn is already running"; use Stop first. Switching to another project stops a running turn.
|
||||
* The web build has no Chat mode.
|
||||
* The canvas shows the run's outlines after the turn ends, when the run is reviewed. They are not drawn live while the agent works.
|
||||
* While the agent works, the canvas shows fading outlines of the layers it touches; the full change review (added, changed, removed) comes after the turn, when the run is reviewed.
|
||||
* Variations ×3 is a prompt for now: the agent makes the variants; there is no side-by-side Keep tag yet.
|
||||
* Over the ACP stdio bridge the chat id does not reach the server, so those runs show under the agent's client name and close after 30 seconds idle instead of at turn end.
|
||||
* The API backend stops after 40 tool rounds in one message and says so.
|
||||
* Codex reports no per-call usage, so the context meter is hidden for it.
|
||||
* Agents need to be installed and signed in with their own tools. Chat does not install them or sign them in for you.
|
||||
|
||||
@@ -60,7 +60,7 @@ papercraft-cli call set_text_content '{"updates":[{"nodeId":"<label layer id fro
|
||||
* `create_instance` returns the instance id and its layers, so you can set text and styles on them. Changes made through `set_text_content` or `update_styles` on an instance are recorded as overrides; everything else follows the main component.
|
||||
* In `write_html`, `<x-paper-instance component-id="<id>" layer-name="Button / Save"/>` places an instance inline.
|
||||
* `get_components` lists the main components of every page (and library components) with `componentKey`, page and how many instances each has.
|
||||
* **Across pages.** Instances can be placed from a main on any page: `create_instance` and `swap_instance` take a `componentId` (`pageId:num`) or a `componentKey` (stable, from `get_components`). Editing a main on page A updates its instances on every other page, one undo step per page. If the main is deleted its instances stay as they are, marked "Main missing" in Assets, where you can detach them. `import_components` still copies a main onto a page, but reuse no longer needs it.
|
||||
* **Across pages.** Instances can be placed from a main on any page: `create_instance` and `swap_instance` take a `componentId` (`pageId:num`) or a `componentKey` (stable, from `get_components`). Editing a main on page A updates its instances on every other page, one undo step per page. If the main is deleted its instances stay as they are, marked "Main missing" in Assets, where you can detach them. `<x-paper-instance component="Name"/>` in `write_html` links the same way when the main is on another page (it used to copy the main onto the page). `import_components` still copies mains onto a page, detached from the source; it is for moving a design system, not for using components.
|
||||
* **Limits.** No variants or properties: model variants as separate components (`Button / Primary`, `Button / Secondary`). `detach_instance` and `swap_instance` exist as tools; in the editor detach with ⌘⌥B.
|
||||
|
||||
## Icons
|
||||
|
||||
|
Before Width: | Height: | Size: 718 KiB After Width: | Height: | Size: 720 KiB |
|
Before Width: | Height: | Size: 628 KiB After Width: | Height: | Size: 675 KiB |
|
Before Width: | Height: | Size: 604 KiB After Width: | Height: | Size: 604 KiB |
|
Before Width: | Height: | Size: 269 KiB After Width: | Height: | Size: 269 KiB |
|
Before Width: | Height: | Size: 173 KiB After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 636 KiB After Width: | Height: | Size: 638 KiB |
|
Before Width: | Height: | Size: 1.0 MiB After Width: | Height: | Size: 1.0 MiB |
|
Before Width: | Height: | Size: 838 KiB After Width: | Height: | Size: 838 KiB |
|
Before Width: | Height: | Size: 691 KiB After Width: | Height: | Size: 694 KiB |
|
Before Width: | Height: | Size: 709 KiB After Width: | Height: | Size: 712 KiB |
|
Before Width: | Height: | Size: 882 KiB After Width: | Height: | Size: 885 KiB |
@@ -59,7 +59,7 @@ function Get-PeHeader([string] $Path) {
|
||||
return @{ Machine = [BitConverter]::ToUInt16($bytes, $pe + 4); Subsystem = [BitConverter]::ToUInt16($bytes, $pe + 0x5C) }
|
||||
}
|
||||
$Machine = switch ($Arch) { 'x64' { 0x8664 } 'x86' { 0x14C } 'arm64' { 0xAA64 } }
|
||||
foreach ($name in @('papercraft.exe', 'papercraft-cli.exe', 'papercraft-mcp.exe')) {
|
||||
foreach ($name in @('papercraft.exe', 'papercraft-cli.exe', 'papercraft-mcp.exe', 'papercraft-relay.exe')) {
|
||||
$exePath = Join-Path $Bin $name
|
||||
if (Test-Path $exePath) {
|
||||
$h = Get-PeHeader $exePath
|
||||
@@ -71,13 +71,13 @@ foreach ($name in @('papercraft.exe', 'papercraft-cli.exe', 'papercraft-mcp.exe'
|
||||
$Stage = Join-Path $TargetDir "windows-package\$Arch"
|
||||
Remove-Item -Recurse -Force $Stage -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $Stage | Out-Null
|
||||
Copy-Item (Join-Path $Bin 'papercraft.exe'), (Join-Path $Bin 'papercraft-cli.exe'), (Join-Path $Bin 'papercraft-mcp.exe') $Stage
|
||||
Copy-Item (Join-Path $Bin 'papercraft.exe'), (Join-Path $Bin 'papercraft-cli.exe'), (Join-Path $Bin 'papercraft-mcp.exe'), (Join-Path $Bin 'papercraft-relay.exe') $Stage
|
||||
foreach ($f in 'LICENSE-MIT', 'LICENSE-APACHE', 'ATTRIBUTION.md', 'README.md') {
|
||||
$p = Join-Path $Root $f
|
||||
if (Test-Path $p) { Copy-Item $p $Stage }
|
||||
}
|
||||
|
||||
& (Join-Path $PSScriptRoot 'sign.ps1') (Join-Path $Stage 'papercraft.exe') (Join-Path $Stage 'papercraft-cli.exe') (Join-Path $Stage 'papercraft-mcp.exe')
|
||||
& (Join-Path $PSScriptRoot 'sign.ps1') (Join-Path $Stage 'papercraft.exe') (Join-Path $Stage 'papercraft-cli.exe') (Join-Path $Stage 'papercraft-mcp.exe') (Join-Path $Stage 'papercraft-relay.exe')
|
||||
|
||||
# ---- MSI ---------------------------------------------------------------------------------------
|
||||
$Msi = Join-Path $Dist "papercraft-$Version-windows-$Arch.msi"
|
||||
@@ -119,9 +119,22 @@ $CliZip = Join-Path $Dist "papercraft-cli-$Version-windows-$Arch.zip"
|
||||
Remove-Item -Force $CliZip -ErrorAction SilentlyContinue
|
||||
Compress-Archive -Path $CliPortable -DestinationPath $CliZip
|
||||
|
||||
# ---- relay zip ---------------------------------------------------------------------------------
|
||||
$RelayPortable = Join-Path $TargetDir "windows-package\papercraft-relay-$Version-windows-$Arch"
|
||||
Remove-Item -Recurse -Force $RelayPortable -ErrorAction SilentlyContinue
|
||||
New-Item -ItemType Directory -Force -Path $RelayPortable | Out-Null
|
||||
Copy-Item (Join-Path $Stage 'papercraft-relay.exe') $RelayPortable
|
||||
foreach ($f in 'README.md', 'LICENSE', 'LICENSE-MIT', 'LICENSE-APACHE', 'ATTRIBUTION.md') {
|
||||
$p = Join-Path $Root $f
|
||||
if (Test-Path $p) { Copy-Item $p $RelayPortable }
|
||||
}
|
||||
$RelayZip = Join-Path $Dist "papercraft-relay-$Version-windows-$Arch.zip"
|
||||
Remove-Item -Force $RelayZip -ErrorAction SilentlyContinue
|
||||
Compress-Archive -Path $RelayPortable -DestinationPath $RelayZip
|
||||
|
||||
# Smoke test
|
||||
Invoke-Native 'papercraft-cli --help' { & (Join-Path $Stage 'papercraft-cli.exe') --help | Out-Null }
|
||||
|
||||
$artifacts = @($Zip, $CliZip)
|
||||
$artifacts = @($Zip, $CliZip, $RelayZip)
|
||||
if (Test-Path $Msi) { $artifacts += $Msi }
|
||||
Get-Item $artifacts | Format-Table Name, Length
|
||||
|
||||
@@ -33,7 +33,7 @@ Allowed spacing values: **4, 8, 12, 16, 24, 32, 48, 64** (and 96 for page sectio
|
||||
## 2. Tokens first (variables)
|
||||
|
||||
* All colors are variables (`var(--name)`), named by role: `bg`, `surface`, `border`, `text`, `text-muted`, `accent`, `on-accent`, `success`, `warning`, `danger`. Two to three surface levels at most (`bg` < `surface` < `surface-2`).
|
||||
* Define at least two modes if the product may be dark or light (`set_variables` with `mode`), and check both: lint contrast in each mode after `set_variable_mode`.
|
||||
* Define at least two modes if the product may be dark or light (`set_variables` with `mode`), and check both: lint contrast in each mode after `set_variable_mode`, or look with `get_screenshot {mode}` without switching. Themes are modes, never copies: one artboard per screen, never a light and a dark duplicate (`lint_design` flags them as `theme-copy`).
|
||||
* Semantic colors carry meaning only: red means error or destructive, green means success. Do not use them decoratively.
|
||||
* If a color you need is not a variable, add the variable, then use it. `lint_design` reports hard-coded colors that equal a variable.
|
||||
* Spacing, radius and type sizes are number variables (`space/md`, `radius/sm`, `font/body`): define them once, in a collection of their own, and bind fields with `bind_variable`. Keep the values on the lists in this skill. Group names with `/` and alias semantic names to base ones (`color/button/bg` to `color/brand/primary`).
|
||||
@@ -78,7 +78,7 @@ Sizes: **12, 14, 16, 20, 24, 32, 40, 48, 64.** `lint_design` flags anything else
|
||||
## 6. Touch targets and density
|
||||
|
||||
* Interactive targets are **at least 44px** tall and wide on touch (mobile, tablet). Make them 44 with padding (12 + 20 + 12), not just a bigger font. On dense desktop UIs 32 to 40 is acceptable; say so, and keep spacing between targets at 8 or more.
|
||||
* `lint_design` checks frames named `Button…`, `Tab /…` and `Tab Bar /…` for height under 44, so **name interactive layers that way**.
|
||||
* `lint_design` checks frames named `Button…`, `Tab /…` and `Tab Bar /…`: under 44px tall on a touch screen (or a component on its own), under 24px on a screen 1024px or wider (WCAG 2.2 AA for pointers). **Name interactive layers that way.**
|
||||
* Mobile: body 16px or larger, side padding 16 to 24, bottom navigation within thumb reach, safe areas (status bar 44, home indicator 34 at the bottom).
|
||||
|
||||
## 7. Components and states
|
||||
|
||||
@@ -21,6 +21,10 @@ papercraft-cli call get_basic_info # fails with exit 1 if the app is not ru
|
||||
* `pageId` defaults to the page the user is viewing, which can change between calls: pass it explicitly. Node ids look like `3fa9c1:14`; never show them to the user.
|
||||
* App closed (server, CI)? `papercraft-cli call --offline ... --project <name>` runs the same tools on the project store and saves after every editing call; `papercraft-cli shell` keeps one session for many calls and reads scripts from stdin. It is refused while the app has that project open. See `docs/agents.md`.
|
||||
* With MCP tools you do not need the CLI; with a shell, `papercraft-cli list` and `papercraft-cli schema <tool>` give the schemas on demand. Pass big JSON on stdin: `... | papercraft-cli call write_html -`.
|
||||
* **Working in the user's open project: use `papercraft-cli --online call …`** (or `PAPERCRAFT_CLI_NO_OFFLINE=1`). Without it, a call made while the app is starting or stuck silently falls back to offline writes on the files. `stderr` warns when the CLI and app versions differ: tell the user, don't ignore it.
|
||||
* **One call at a time per agent.** No background loops or parallel batches against the app; a backlog of screenshot calls makes the app unresponsive for every agent.
|
||||
* **App stops answering** ("did not answer in time", 0% CPU): usually a macOS dialog (a privacy prompt such as "access files on a removable volume") or a modal in the app. Tell the user and wait; never kill, quit or restart their PaperCraft.
|
||||
* **See what the user sees**: `get_basic_info` / `get_selection` include `viewport {pageId, rect, zoom}`. `set_viewport {pageId, nodeIds | rect, zoom?}` switches page and frames things for the user; it refuses while they are moving the canvas (pass `force` only when they asked you to show something).
|
||||
|
||||
## 2. The loop: build, look, lint, fix
|
||||
|
||||
@@ -34,6 +38,23 @@ papercraft-cli call get_basic_info # fails with exit 1 if the app is not ru
|
||||
|
||||
Lint passes are necessary, not sufficient: contrast and grids say nothing about composition. The screenshot is the real test.
|
||||
|
||||
## 2b. Big jobs and several agents on one project
|
||||
|
||||
Lessons from redesigning a 3-page, 130-screen project with up to six agents at once:
|
||||
|
||||
* **Split by section, not by page.** Give each agent named sections (frame ids) and forbid the rest. New sections go below the current lowest one; re-read positions right before creating, since another agent may have just added one.
|
||||
* **Section layout**: a section frame holds a title and rows of screen slots (a route label above a fixed-size screen). Keep 640 px between screens and rows and 1600 px between sections, so notes fit beside screens. Stack sections in one column.
|
||||
* **Recreate, then delete.** Build the new screen next to the old one, finish it, then delete the old one, and record the deleted ids and names in the report. A cleanup agent once deleted buttons and lost icons by editing in place.
|
||||
* **Reuse the design system across pages**: `create_instance` / `swap_instance` / `<x-paper-instance>` take a main from any page and stay linked. Never `import_components` to use a component: that copies mains onto the page, and instances made from the copies stop following the design system. `lint_design` flags hand-made look-alikes as `not-an-instance` (a different icon is a different component); fix with `write_html` mode replace and the `<x-paper-instance>` it names.
|
||||
* **QA pass after a build** (screen by screen at scale 1, one call at a time): text layers with stale fixed widths clip labels ("In prog", "New sess"): set `width: fit-content`. Sidebars and panels fixed at 900 px stop short on taller screens: make them fill the height and let the screen hug with a 900 px minimum. Tags and chips overflowing their box, cards overlapping buttons, footers not pinned, leftover `Text` / `Frame` names, gendered copy about people whose gender is unknown (use their name or neutral wording).
|
||||
* **Report per agent** in `specs/<feature>/<agent>-report.md`: what changed (ids), why (playbook rule ids from `playbook search`), open questions. Unknown product facts are written as questions for the team, never invented.
|
||||
|
||||
## 2c. Annotations
|
||||
|
||||
* Read the page's template first (`get_annotation_template {pageId}`), then one batched `annotate {pageId, notes:[{node, text, type}]}` per section, then `annotate {pageId, frame: <section id>, tidy: true}` (returns `{tidied: n}`; 0 means already placed).
|
||||
* Content: the first line is the complete rule in 60 characters or fewer; the body is condition → outcome with values as `Field: value` lines; attach to the exact element, not the screen; few high-value notes; unknowns as `? (team)`. Rules: `playbook rule ANN`.
|
||||
* Check placement with `get_screenshot {nodeId: <screen slot>, annotations: true, scale: 0.5}`; plain screenshots do not show notes. Cards must sit beside their target off any screen, lines must not cross other screens. Below 60% zoom the canvas shows notes as badges.
|
||||
|
||||
## 3. Naming
|
||||
|
||||
* Layers: `Noun` or `Component / Variant` (`Button / Primary`, `Tab / Today`, `Card / Task`, `Icon / gear`). Slashes group the layers panel.
|
||||
@@ -79,17 +100,32 @@ Full flows: [recipes.md](references/recipes.md).
|
||||
| Know the project, pages, artboards | `get_basic_info`, `get_tree_summary`, `get_children` |
|
||||
| Know what is selected | `get_selection` |
|
||||
| Inspect one node | `get_node_info`, `get_computed_styles`, `get_jsx` |
|
||||
| Find nodes by text or style | `find_nodes` |
|
||||
| See it | `get_screenshot` (`nodeId`, `scale` 1 or 2) |
|
||||
| Find nodes by text or style | `find_nodes` (text contains the phrase, any case, every page) |
|
||||
| Read all copy in a subtree | `get_tree_summary {nodeId, fullText: true}` (depth counts from the node: rows under a "Screens" frame need depth 3) |
|
||||
| See it | `get_screenshot` (`nodeId`, `scale` 1 or 2, `annotations: true` to include notes, `mode` to preview a theme without switching) |
|
||||
| Show the user something | `set_viewport` (page switch, fit, zoom) |
|
||||
| Document rules on the design | `annotate`, `get_annotation_template`, `get_annotations`, `update_annotation`, `delete_annotations` |
|
||||
| New screen | `create_artboard` (`width` and `height` required) |
|
||||
| Add content | `write_html` (`insert-children` or `replace`) |
|
||||
| Change styles or text | `update_styles`, `set_text_content`, `rename_nodes` |
|
||||
| Whiteboard shapes, sticky notes, arrows | `create_shape` |
|
||||
| Copy, move, delete | `duplicate_nodes`, `move_nodes`, `delete_nodes` |
|
||||
| Tokens | `get_variables` (`format` css, json or tailwind), `set_variables` (`collection`, `type`, `mode`, `alias`), `create_collection`, `delete_variables`, `set_variable_mode`, `bind_variable` |
|
||||
| Reuse | `create_component`, `create_instance`, `swap_instance`, `get_components`, `<x-paper-instance>` |
|
||||
| Reuse | `create_component`, `create_instance`, `swap_instance` (also a nested badge inside a row instance: a swap that survives main edits), `get_components`, `<x-paper-instance>` |
|
||||
| Component properties | One component with properties instead of one per state: `set_component_props` (`text`, `bool`, `icon`, `enum` options in the overrides format; templates `text`, `active` for tab bars and navs, `status` with [{value, color}]), then `set_instance_props {nodeIds, values}` or `<x-paper-instance component="Badge" props='{"Status":"Rejeitada"}'/>`; `get_components` lists them |
|
||||
| Merge per-state components | `merge_variants {componentIds, name}` turns `Tab bar / Hoje`, `Tab bar / Lojas`... into one `Tab bar` with an enum, moves every instance and deletes the emptied variants |
|
||||
| Prototype | `set_prototype_links {links:[{nodeId, to}]}`: back arrows, rows and tabs go to their screens in Present mode |
|
||||
| Screens | Fixed size (390x844, 1440x900...); content longer than the screen: `overflow: auto` on the screen frame, so it keeps its size and the overflow scrolls on the canvas and in Present (inspector: Scroll overflow; page: Screens scroll) |
|
||||
| Project | `rename_file {name}` (open project stays open), `move_nodes {toPageId}` keeps instances linked and notes attached |
|
||||
| Shared libraries | `publish_library`, `list_libraries`, `enable_library`, `update_library`, `disable_library` |
|
||||
| Icons | `search_icons`, `<x-paper-icon name="set:name">`; CLI: `papercraft-cli icons add\|list\|search` |
|
||||
| Design in a library's look, hand off its code | `apply_ui_kit` (shadcn, antd, reactbits, egui), then `<x-paper-instance>` of its components; `set_code_link` for a component of your own; `get_jsx` / `get_design_spec` then write `<Button variant="outline">` instead of divs |
|
||||
| Avatars, more icons, photos, flags, QR | `<x-plugin use="avataaars\|dicebear\|iconify\|picsum\|flags\|qr" …/>` inside `write_html` (plugins the user turned on) |
|
||||
| References and art | `fetch_reference_images`, `zoom_image`, `generate_image` (when the user turned image generation on) |
|
||||
| Ask the user | `ask_user` (clickable answers; end your turn after it) |
|
||||
| Hand work to the helper agent | `delegate` (`role` qa, read or build, a self-contained `task`; only when Settings, Chat, Helper is set) |
|
||||
| Design rules (design-playbook) | `design_rules` (`query` or `rule` id; needs the `playbook` CLI; cite ids in the report) |
|
||||
| Report a PaperCraft problem | `report_issue` (a draft the user sends; synthetic repro only) |
|
||||
| Check quality | `lint_design` |
|
||||
| Export | `export` (PNG, SVG, PDF to files), `export_combined_pdf` |
|
||||
| Point at things for the user | `select_nodes` (`zoom: true`) |
|
||||
@@ -113,8 +149,9 @@ Full flows: [recipes.md](references/recipes.md).
|
||||
* **Text inside nested frames**: `set_text_content` needs the Text node id. Walk `get_children` down from the instance; instances copy the main's structure (`Meta > Label`, not a flat list).
|
||||
* **No icon-colour follow for stroke sets** (Lucide, Tabler): the colour is frozen at write time, so a mode switch (Mocha to Latte) leaves icons as they were. Use a fill set (Phosphor, Material) when a design must switch modes.
|
||||
* **Pages**: `create_page`, `rename_pages`, `delete_pages` (needs `confirm: true` when the page has layers; the last page stays). `move_nodes` accepts `toPageId` to copy between pages.
|
||||
* **`get_screenshot` of `root` on a wide page** is scaled to fit and unreadable. Screenshot one artboard at a time. Hoisted components are not inside an artboard screenshot.
|
||||
* **CLI saves images to the cwd** as `<tool>.png`; newer builds take `-o FILE`. Run the CLI from a scratch directory, or pass `-o`, and rename after.
|
||||
* **`get_screenshot` of `root` on a wide page** is scaled to fit and unreadable. Screenshot one artboard (or one screen slot) at a time. Hoisted components are not inside an artboard screenshot.
|
||||
* **CLI saves images to the cwd** as `<tool>.png`, or to `-o FILE`. The CLI writes the file itself; on older builds an `out` path made the app write it and could freeze the app behind a macOS privacy prompt, so update the CLI with the app (`tools/install-local.sh` installs both).
|
||||
* **`swap_instance` renames the instance root to the new main's name** unless the name was overridden. Re-apply custom layer names after a batch swap (`rename_nodes`).
|
||||
|
||||
## 7. Troubleshooting
|
||||
|
||||
@@ -125,7 +162,10 @@ Full flows: [recipes.md](references/recipes.md).
|
||||
| `write_html` reports `ignoredStyles` | You used a CSS property that has no equivalent (`margin`, `box-shadow`, `transform`...). Replace it, see [html-subset.md](references/html-subset.md). |
|
||||
| Content is cut off | The artboard height is fixed. `update_styles` with `height: "fit-content"` or a larger number. |
|
||||
| A color did not change with the theme | It was written as hex, or it is a stroke or gradient. Use `var(--name)` on fills and text; strokes need `update_styles` after a mode switch. |
|
||||
| `create_instance` fails | The component is on another page. Instances must be on the component's page. |
|
||||
| `create_instance` fails | Wrong `componentId` / `componentKey` (read `get_components`). Mains on other pages and enabled libraries work; no import needed. |
|
||||
| A page is full of copied components at y=0 | Something imported mains onto the page (`import_components`, or old `write_html` builds). Swap their instances to the design-system mains (`swap_instance {componentKey}`), check a screenshot, then delete the unused copies. |
|
||||
| `papercraft-cli --online` says "did not answer in time" | The app is blocked: look for a macOS dialog or an in-app modal and ask the user to answer it. Don't retry in a loop and don't restart the app. |
|
||||
| A tool name is unknown, or the docs list tools the CLI lacks | The CLI is stale. `papercraft-cli list` reads the app's live list; reinstall the CLI with the app. |
|
||||
| Icon does not appear | The set is not installed: `papercraft-cli icons add phosphor`, then `papercraft-cli icons search <words>` for the exact `set:name`. |
|
||||
| Layer panel is a pile of `Frame` | You skipped `layer-name`. Fix with `rename_nodes` (batch) before finishing. |
|
||||
| Text looks tiny in the screenshot | Screenshots default to scale 1. Use `scale: 2`. |
|
||||
|
||||
@@ -13,7 +13,7 @@ How to keep designs small, keep design and code in step, and spend few tokens do
|
||||
|
||||
* Build every repeated thing once: `create_component`, then `create_instance` or `<x-paper-instance>`. Never paste markup twice. A screen is mostly instances plus layout frames.
|
||||
* No raw colours, radii, spacing or shadows in `write_html`: `var(--token)` only. Changing a token then updates every page, and `get_design_spec` reports token names, which is what code needs.
|
||||
* Reuse across pages with `import_components` (instances stay linked) or `<x-paper-clone node-id="page:id">`. Do not rebuild a component per page.
|
||||
* Reuse across pages with `create_instance {componentKey}` or `<x-paper-instance component="Name"/>` in `write_html`: a main on any page (or an enabled library, for `create_instance`) works directly and the instance stays linked to it. Do not `import_components` for that (it makes detached copies; it is for moving a design system). Do not rebuild a component per page.
|
||||
* One design system per brand: `export_design_system` writes an OpenDesign package (variables, modes, tokens); `import_design_system` loads it into another project. That is the sync path between files and apps.
|
||||
* Write one visual group per `write_html` (a header, a row), with `layer-name` on every box. Fewer, larger writes cost fewer round trips; groups that are too big make one bad style hard to find.
|
||||
|
||||
|
||||
@@ -60,14 +60,14 @@ Delete one or more pages. The last page of a project cannot be deleted. A page t
|
||||
|
||||
### `get_basic_info` (read-only)
|
||||
|
||||
Essential context about the open project: project name, the page, node count, artboards (top-level frames) with ids, sizes and world positions, other top-level layers, fonts, and `pages` with the active one marked isActive.
|
||||
Essential context about the open project: project name, the page, node count, artboards (top-level frames) with ids, sizes and world positions, other top-level layers, fonts, `pages` with the active one marked isActive, and `viewport` ({pageId, rect: [x, y, w, h] world, zoom}: what the user sees).
|
||||
- worldX/worldY are world positions; x/y are relative to the parent.
|
||||
- Pass pageId to inspect another page without switching the user's view.
|
||||
|
||||
|
||||
### `get_selection` (read-only)
|
||||
|
||||
The selected layers on the page the user is viewing: ids, names, types, sizes, positions and the artboard each belongs to.
|
||||
The selected layers on the page the user is viewing: ids, names, types, sizes, positions and the artboard each belongs to, plus `viewport` ({pageId, rect: [x, y, w, h] world, zoom}).
|
||||
|
||||
|
||||
### `get_node_info` (read-only)
|
||||
@@ -94,9 +94,20 @@ Compact indented text tree of a subtree: type, name, id and size per node. Much
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| `depth` | number | | Maximum depth (default 3, max 10). |
|
||||
| `depth` | number | | Levels shown, counting nodeId (or each top-level layer for "root") as level 1 (default 3, max 10). Deeper levels collapse to "… N children". |
|
||||
| `fullText` | boolean | | Show each text layer's complete text instead of its first 40 characters (for copy review). |
|
||||
| `nodeId` | string | yes | Root of the summary, or "root". |
|
||||
|
||||
Depth counts from the node you pass, not from the page. For an artboard `Home` with a `Screens` frame holding rows, `{"nodeId": "<Home id>", "depth": 2}` stops at `Screens`:
|
||||
|
||||
```
|
||||
frame "Home" [3fa9c1:1] 1440×900
|
||||
frame "Screens" [3fa9c1:2] fill(1440)×hug(820)
|
||||
… 3 children
|
||||
```
|
||||
|
||||
The rows inside `Screens` are level 3, so they need `depth: 3` from `Home` (the default), or `depth: 2` from `Screens` itself. With `nodeId: "root"` every artboard is level 1, so the same rows also need `depth: 3`.
|
||||
|
||||
|
||||
### `get_jsx` (read-only)
|
||||
|
||||
@@ -151,7 +162,7 @@ CSS styles (camelCase) of one or more nodes, as {nodeId: styles}.
|
||||
|
||||
### `find_nodes` (read-only)
|
||||
|
||||
Find nodes by text content and/or computed style. Searches the whole project by default; pass pageId for one page or nodeId for a subtree. `textValue` matches text case-insensitively with * wildcards anchored to the whole value. `filters` are {styleName, styleValue} pairs (AND), values may use * wildcards; colors match by value.
|
||||
Find nodes by text content and/or computed style. Searches the whole project by default; pass pageId for one page or nodeId for a subtree. `textValue` finds text containing it, ignoring case and line breaks; with * wildcards it must match the whole value ("Get *"). `filters` are {styleName, styleValue} pairs (AND), values may use * wildcards; colors match by value.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
@@ -195,6 +206,8 @@ Render a node (or the whole page when nodeId is omitted or "root") to a PNG and
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| `annotations` | boolean | | Also draw the note cards, badges and leader lines of notes on the captured layers; the image grows to include the cards. |
|
||||
| `mode` | string | | Render in this variable mode (e.g. "Light") without switching the document: preview every theme of one artboard while other agents keep working in the current mode. |
|
||||
| `nodeId` | string | | The node to capture; omit for the whole page. |
|
||||
| `out` | string | | Optional file path to save the PNG to (also used by papercraft-cli). |
|
||||
| `scale` | number | | Render scale: 1 (default) or 2. |
|
||||
@@ -426,7 +439,7 @@ Main components of every page (or one with `pageId`): id, name, `componentKey` (
|
||||
|
||||
### `import_components`
|
||||
|
||||
Copy the main components anywhere on `fromPageId` (including inside artboards, typically on a `Design System` page) onto this one, so you can place instances here with create_instance or <x-paper-instance component="Name"/> / component-id="ID". Components with the same name already here are skipped. Variables and tokens are already shared by every page; components are copies, not live links.
|
||||
Copies mains onto this page (detached from the source); only for moving a design system, not for using components: create_instance and swap_instance take a main on any page or an enabled library directly, with no import. Copies the main components anywhere on `fromPageId` (including inside artboards); components with the same name already here are skipped. Variables and tokens are already shared by every page.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
@@ -446,7 +459,7 @@ Turn a frame or artboard into a main component. Build reusable parts (button, ro
|
||||
|
||||
### `create_instance`
|
||||
|
||||
Place an instance of a component. Edit text and styles on the instance's descendants with set_text_content / update_styles (they become overrides); edits to the main component flow to every instance. Returns the instance id and its text layers.
|
||||
Place an instance of a component. componentKey may be a component on any page or an enabled library; no import needed (the instance stays linked to that main). Edit text and styles on the instance's descendants with set_text_content / update_styles (they become overrides); edits to the main component flow to every instance. Returns the instance id and its text layers.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
@@ -461,7 +474,7 @@ Place an instance of a component. Edit text and styles on the instance's descend
|
||||
|
||||
### `swap_instance`
|
||||
|
||||
Swap an instance root to point at a different main component (any page), preserving overrides on descendants matched by name and layer type.
|
||||
Swap an instance root to point at a different main component, preserving overrides on descendants matched by name and layer type. componentKey may be a component on any page or an enabled library; no import needed.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
@@ -544,6 +557,62 @@ Stop using a library: bindings to its variables are detached (values kept), its
|
||||
| `libraryId` | string | yes | |
|
||||
|
||||
|
||||
## Annotations
|
||||
|
||||
### `annotate`
|
||||
|
||||
Add notes to node ids or exact layer names on a page in one undo step. First text line is label; remaining lines are body. Type is an allowed category key (default rule); put fields as "Name: value" lines in text. Placement is automatic (tidy re-lays-out). Returns [number, node, type, label] rows; tidy alone returns {tidied: moved}.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| `frame` | string | | With tidy: only the notes on this frame. |
|
||||
| `notes` | array of object | | `{node, text, type?}`. |
|
||||
| `pageId` | string | | |
|
||||
| `tidy` | boolean | | Re-layout all notes (of frame, else the page) as part of this undo step; notes may then be omitted. |
|
||||
|
||||
Check the placement with `get_screenshot {"nodeId": "<frame>", "annotations": true}`.
|
||||
|
||||
### `get_annotation_template` (read-only)
|
||||
|
||||
Read the page's annotation template: [type key, guidance, field names] for each allowed type. Call once before annotate.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| `pageId` | string | | |
|
||||
|
||||
|
||||
### `set_annotation_template`
|
||||
|
||||
Switch the annotation template by id or name, for one page or the whole project. Annotate only accepts the template's types.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| `pageId` | string | | |
|
||||
| `scope` | string | | `page` or `project`. |
|
||||
| `template` | string | yes | |
|
||||
|
||||
|
||||
### `upsert_annotation_template`
|
||||
|
||||
Create or replace an annotation template, optionally defining new categories (key, name, #hex color, icon name) first. Reuse existing keys freely. One undo step for the edit; setAs page adds a second. Returns {id, name, types}.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| `categories` | array of object | | Category styles `{key, name, color, icon}`; an existing key is updated, a new key needs all four. |
|
||||
| `pageId` | string | | |
|
||||
| `setAs` | string | | `project` or `page`. |
|
||||
| `template` | object | yes | `{id?, name, display?: full or badges, categories: [{key, enabled?, guidance?, fields?}]}`. |
|
||||
|
||||
|
||||
### `delete_annotation_template`
|
||||
|
||||
Delete an annotation template by id or name. The last template cannot be deleted; a project default pointing at it reverts to Product spec.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| `template` | string | yes | |
|
||||
|
||||
|
||||
## Session
|
||||
|
||||
### `get_feedback` (read-only)
|
||||
@@ -581,7 +650,7 @@ Mark notes on a layer resolved, with a short reply saying what you changed. With
|
||||
|
||||
### `select_nodes`
|
||||
|
||||
Select nodes on the page the user is viewing so they can see what you mean; zoom: true also frames them in the viewport. An empty list clears the selection.
|
||||
Select nodes on the page the user is viewing so they can see what you mean; zoom: true also frames them in the viewport (at most 100%). An empty list clears the selection. For another page or a closer zoom use set_viewport.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
@@ -589,6 +658,20 @@ Select nodes on the page the user is viewing so they can see what you mean; zoom
|
||||
| `zoom` | boolean | | |
|
||||
|
||||
|
||||
### `set_viewport`
|
||||
|
||||
Move the user's view: switch to pageId (as clicking it in the page list does) and fit nodeIds or a world rect, optionally at an explicit zoom (0.01 to 256). With only zoom it zooms about the view's centre. Refused while the user is moving the canvas (within 10 s of their last pan or zoom) unless force: true. Returns the new viewport ({pageId, rect: [x, y, w, h] in world units, zoom}); get_basic_info and get_selection report the current one.
|
||||
|
||||
| Argument | Type | Required | Notes |
|
||||
|---|---|---|---|
|
||||
| `force` | boolean | | Move the view even if the user is panning or zooming. |
|
||||
| `nodeIds` | array of string | | Nodes to fit in the view. |
|
||||
| `padding` | number | | Screen margin when fitting, in px (default 64). |
|
||||
| `pageId` | string | | Page to show; defaults to the page of nodeIds, else the current one. |
|
||||
| `rect` | array of number | | World rect [x, y, width, height] to fit, instead of nodeIds. |
|
||||
| `zoom` | number | | Explicit zoom level (1 = 100%); centres the nodes or rect at this zoom instead of fitting. |
|
||||
|
||||
|
||||
### `undo`
|
||||
|
||||
Undo the last change on a page (the user's or an agent's).
|
||||
|
||||
|
After Width: | Height: | Size: 31 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 9.2 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
@@ -0,0 +1,24 @@
|
||||
# Gaps found while driving PaperCraft on the Interview project (2026-10-21..23)
|
||||
|
||||
Observed by the orchestrating agent and its subagents through MCP / papercraft-cli on the user's Mac.
|
||||
|
||||
## MCP / agent side
|
||||
1. `get_screenshot` never draws annotations, so no agent can verify note placement; every check needed a macOS screen capture of the live window.
|
||||
2. No tool to switch the page the user is viewing or to set the viewport (zoom / pan / fit a rect); `select_nodes {zoom:true}` only works on the active page and caps zoom at 1.0.
|
||||
3. Agents can't see what the user sees (viewport rect, zoom, selection on screen) except `get_selection`.
|
||||
4. `~/.local/bin/papercraft-cli` was stale (schema/list missing new tools) while `call` reached the app — CLI and app versions drift with no warning.
|
||||
5. Applying a code change to the running app needs quit + reinstall + reopen (no dev reload / no "restart and reopen project" path).
|
||||
6. Long batched edits (thousands of bindings, renames) run as many separate calls; no progress shown to the user in chat mode for CLI-driven agents beyond the "Working…" pill.
|
||||
7. Cleanup agents made destructive mistakes (deleted button copies, lost icons on LG buttons) with no per-agent review/diff of what changed; undo is global, not per agent run.
|
||||
8. Tidy/placement was blind to zoom-dependent card size (fixed), to screens inside sections (fixed), and lines detoured inside screens (fixed).
|
||||
9. `get_tree_summary` depth semantics surprised scripts (rows under "Screens" needed depth 3).
|
||||
|
||||
## App / 0.6.2 follow-ups already noted
|
||||
- Run card stale after undo; lingering "Working" pill; Stop reported as error.
|
||||
- No-GPU panic should be a readable error; Wayland clipboard without DISPLAY.
|
||||
- Email truncation (no text-overflow ellipsis).
|
||||
- 13.7 s open once after install (first run, not reproduced).
|
||||
10. `find_nodes` text search returned nothing for strings that exist on the page (Candidate redesign agent, 2026-10-23) — reproduce and fix.
|
||||
11. `get_tree_summary` truncates long text to ~40 chars with no option for full text, so copy review across many screens is impossible without per-node reads.
|
||||
12. `annotate {tidy:true}` with no notes returns `[]`, which agents read as failure (fix in A1: `{tidied: n}`).
|
||||
13. When the app stops mid-run the CLI silently falls back to offline writes; several agents then write the same project files concurrently with no lock (lost-update risk). Also a CLI call during app start-up reports "app not running" and goes offline.
|
||||
@@ -0,0 +1,82 @@
|
||||
# Chat ↔ canvas: research
|
||||
|
||||
How canvas + AI products let a user act through chat and point at the canvas, and what PaperCraft already has. Researched 2026-10-23. Secondary sources (forums, reviews) are marked; treat their details as approximate.
|
||||
|
||||
## 1. What PaperCraft does today (code)
|
||||
|
||||
* **One path to the canvas: tool calls.** Every backend (Claude Code, Codex, ACP, API endpoints) edits only through the 69 MCP tools. CLI/ACP agents reach them over HTTP MCP (`src/mcp/server.rs`); API endpoints call `crate::mcp::exec` on the UI thread (`src/chat/tools.rs`, `ToolDispatch`). The user can't trigger anything in-process from chat except sending text.
|
||||
* **Context the agent gets:** `skills/papercraft/SKILL.md` (14 KB) plus the project name and folder (`ChatState::system_prompt`, `src/ui/chat.rs:602`). There is no design-system digest, no viewport and no screenshot of what the user sees.
|
||||
* **Pointing:** the `@ Selection` chip (⌘L), `add_selection` in `src/ui/chat.rs`. It sends `[Selection] <label> (<node ids>)` plus the annotations on those layers (`selection_notes`). It has no image, no region and no point. Annotations' "Annotate with AI" (`src/ui/annotations.rs:557`) is the one canvas-to-chat hand-off: it drafts a prompt, adds a chip and switches to Chat mode, but does not send.
|
||||
* **Showing agent work:** the activity pill (`agent_pill`, `AGENT_PILL_FRAME_MS`, `agent_verb` in `src/ui/mod.rs:310-360`) shows a verb only, with no location. Canvas follow (`follow`/`pan_to`, `src/ui/chat.rs:1056`) pans to the node in the call args, on the active page only. The review overlay (`review_overlay`, `src/ui/mod.rs:1929`) draws added/changed/removed outlines, but only after the run ends and is opened for review (docs/chat.md, Limits).
|
||||
* **Runs:** `src/runs.rs` has one global `Tracker`. A run is closed by a 30 s idle, `finish_working_on_nodes` or the end of a chat turn. Each run has auto-versions per page, plus `revert`, `revert_all` (checkpoint "Restore to here") and `revert_change` (one change). The chat has a Checkpoint row and a Run card (Review/Revert/Accept). MCP calls carry no client identity (`call_tool` ignores `RequestContext`), so parallel subagents land in one run (gap 7).
|
||||
* **Screenshots:** `get_screenshot` renders nodes with `io::png::render`. Annotations are an egui overlay (`src/ui/annotations_canvas.rs`), so they never appear (gap 1). `src/model/annotation_layout.rs` (new) already gives pure card sizes, placement and leader routing in world units.
|
||||
* **Camera tools:** `select_nodes {zoom}` works only on the active page and fits with a 1.0 zoom cap (`src/mcp/exec.rs:5266`). There is no page switch and no viewport read.
|
||||
* **Design-system data that already exists:** `get_variables` (vars, tokens, modes; css/json/tailwind), `get_components` (page mains with use counts plus `ed.library_docs` mains), `get_annotation_template`, `lint_design {fix}` with `lint::autofix` (rules: text-size, type-scale, spacing-grid, touch-target, layer-name, hard-coded-color), `get_design_spec`, `diff_frames`, `swap_instance`, `bind_variable`. There are no component variants in the model.
|
||||
* **Rendering stack:** custom `egui_winit` + `egui_wgpu` (`src/app.rs`, `src/render/frame.rs`), not eframe, so `ViewportCommand::Screenshot` is not handled. `render/gpu.rs` already has `start_readback` (texture to buffer).
|
||||
|
||||
## 2. Products
|
||||
|
||||
### tldraw: agent starter kit, Make Real, fairies
|
||||
* Agent context: the user message, the **selection, the viewport**, a screenshot of the agent's view, a simplified shape list in view, **off-screen shapes summarised as clusters (bounds + count)**, recent user actions, and lint warnings. The user can add shapes, **a point or an area** as extra context. ([starter kit](https://tldraw.dev/starter-kits/agent), [AI docs](https://tldraw.dev/docs/ai))
|
||||
* The agent **moves its own viewport** to look at parts of the canvas. Actions stream in and apply one at a time. Each action shows in chat with an icon and a one-line description. **Modes** (working, critique) limit the tool set per task. ([v4.4 release](https://tldraw.dev/releases/v4.4.0))
|
||||
* Make Real: select shapes, click one button, and the selection's screenshot goes to the model. You iterate by drawing on top of the result. ([talk summary](https://gitnation.com/contents/make-real-tldraws-accidental-ai-play))
|
||||
* Fairies (Dec 2025 experiment): agents shown as small characters on the canvas. **Where an agent is shows what it is working on**, and several run concurrently on shared space. ([talk](https://gitnation.com/contents/agents-on-the-canvas-with-tldraw), [tldraw offline](https://tldraw.dev/blog/tldraw-offline))
|
||||
* Works: viewport and region as first-class context, a cheap off-screen summary, visible presence.
|
||||
|
||||
### Figma: AI, Make, MCP
|
||||
* Quick actions (⌘K): Rename layers (only default-named layers), Make prototype, Make designs. These are **one-click scoped actions on a selection**, with no chat. ([quick actions](https://help.figma.com/hc/en-us/articles/23570416033943-Use-quick-actions), [Figma AI](https://www.figma.com/blog/introducing-figma-ai/))
|
||||
* First Draft can't use your own design system; it uses Figma-built libraries. ([help](https://help.figma.com/hc/en-us/articles/23955143044247))
|
||||
* **Make kits**: components and styles bundled with **usage guidelines**, so the model learns *how* to use the system, not just what is in it. Attachments (PDF, CSV/JSON, SVG) ground prompts in real data. ([blog](https://www.figma.com/blog/introducing-make-kits-and-make-attachments/))
|
||||
* Best practices for AI to read a DS: semantic slash names, larger reusable blocks (cards, headers) over atoms, auto layout with sizing intent, full variant and property sets, variables for every color, space, radius and type, and a usage-examples page. ([help](https://help.figma.com/hc/en-us/articles/38978644498199-Best-practices-to-help-Figma-AI-understand-your-design-system))
|
||||
* Failure mode reported by users: Make "only recognises a random subset of components" and "created new ones that looked like my components". (forum, 2025: [1](https://forum.figma.com/suggest-a-feature-11/figma-make-not-using-my-library-components-42235/index1.html), [2](https://forum.figma.com/share-your-feedback-26/figma-make-library-support-42423)) The lesson: putting the library "in context" is not enough. Reuse must be checked.
|
||||
* Dev Mode MCP: `get_variable_defs` returns the variables used **in the selection**, Code Connect maps nodes to code components, and `create_design_system_rules` writes a rules file for agents. Selection-based prompting works only with the desktop server. ([guide](https://github.com/figma/mcp-server-guide))
|
||||
* Works: selection-scoped context, DS rules as a compact digest, one-click actions.
|
||||
|
||||
### Cursor Design Mode (browser)
|
||||
* Click an element to target it. **Shift+drag selects an area.** You can draw circles or boxes over a **frozen frame** of the viewport, and voice is accepted. ⌘L adds to chat, ⌥+click adds without the comment step. The agent gets element identity (xpath, component, computed styles) **plus a screenshot**. You can queue several edits while agents run. ([docs](https://cursor.com/docs/agent/design-mode))
|
||||
* Agent checkpoints: a timeline in the chat, preview at a point, restore. Per-file Keep/Undo appears in the review panel. Reject bugs that deleted whole files show the risk of coarse undo. ([forum](https://forum.cursor.com/t/per-change-keep-undo-buttons-missing-after-agent-edits-only-undo-all-available/158983), [forum](https://forum.cursor.com/t/cursor-deletes-entire-file-when-changes-are-rejected/151760))
|
||||
* Pain points (forum): a new pick overwrote the previous one, and the chip always lands at the end of the prompt. ([1](https://forum.cursor.com/t/new-visual-editor-prevents-adding-multiple-elements-in-chat-box/145962), [2](https://forum.cursor.com/t/insert-browser-tab-element-selection-at-cursor-in-composer-not-always-at-end/167268))
|
||||
* Works: identity + pixels together, area selection, multiple chips.
|
||||
|
||||
### v0 design mode / Lovable visual edits
|
||||
* v0: hover highlights and click selects. The **selected element shows as a badge in the prompt**, and the prompt is scoped to it. Direct panel edits are free; prompts cost tokens. A later version added annotations on elements **sent as one batched prompt**. ([v0 docs](https://v0.app/docs/design-mode), [changelog summary](https://www.createwith.com/tool/v0/updates/v0-launches-design-mode-with-visual-editing-and-layer-management))
|
||||
* Lovable: select one or more elements (⌘-click for multi), then describe the change. The selection is the context. ([docs](https://docs.lovable.dev/features/preview-toolbar))
|
||||
* Works: two lanes, deterministic edits for free and prompts for intent. Comments become a batch prompt.
|
||||
|
||||
### Google Stitch
|
||||
* **Annotate**: draw a box around an element, type in a bubble, and only that element changes. You can also circle or write notes on the screen. **Variations**: select a screen and generate N (default 3) side by side, with a creativity range. "Create variant" keeps the original. ([review](https://designproject.io/blog/google-stitch-review/), [guide](https://tutorialsdojo.com/what-google-stitch-actually-does-and-where-it-falls-short/), secondary)
|
||||
* Works: on-canvas prompt bubble, variations as siblings on the canvas, never overwriting.
|
||||
|
||||
### Canva Magic Edit
|
||||
* Brush the area, then prompt one change per pass. Canva advises against giving size in words; the brushed area carries it. ([help](https://www.canva.com/help/using-magic-edit/))
|
||||
* Works: the region carries the spatial intent, so the prompt stays short.
|
||||
|
||||
### Miro Sidekicks, Framer, Excalidraw, Penpot
|
||||
* Miro: select clusters, then ask a task-specialised "Sidekick" (persona/mode) to transform them, for example into sticky notes. It has an MCP server for outside agents. ([TrustRadius](https://tr-web.dev.trustradius.com/products/miro/details), [MCP](https://miro.com/pl/ai/mcp/), secondary)
|
||||
* Framer Wireframer: prompt a layout, then keep iterating in chat or jump to the canvas. Workshop builds components that match the site's style. ([update](https://www.framer.com/updates/wireframer))
|
||||
* Excalidraw: AI is in the paid tier (text-to-diagram, wireframe-to-code). Community MCP servers give agents a live canvas. ([listing](https://www.stork.ai/de/excalidraw), [mcp_excalidraw](https://docsearch.algolia.com/mcp/docs/repo/yctimlin/mcp_excalidraw))
|
||||
* Penpot: experimenting with MCP for agents that read and edit design data. ([summary](https://mcpmarket.com/es/news/9fad1b76-0f9d-4411-b557-348e8f85888c), secondary)
|
||||
|
||||
## 3. Patterns, ranked by value for PaperCraft
|
||||
|
||||
| Pattern | Seen in | PaperCraft today | Gap |
|
||||
|---|---|---|---|
|
||||
| Selection as scoped context, with pixels | Cursor, v0, Lovable, Figma MCP | `@ Selection` ids + notes | no image, no region |
|
||||
| Area/lasso or point as context | Cursor, tldraw, Stitch, Canva | none | missing |
|
||||
| One-click scoped actions on selection | Figma ⌘K, Lovable panel | Annotate menu only | add Optimize, Bind tokens, Rename, Fix lint |
|
||||
| Deterministic lane vs model lane | v0, Lovable | `lint_design {fix}` exists, not in UI | expose without the LLM |
|
||||
| DS digest in context + reuse check | Figma Make kits, `create_design_system_rules` | only the skill text | add a digest and a reuse lint |
|
||||
| Agent viewport + presence | tldraw agent, fairies | pan follow + a verb pill | show where; let the agent read and set the view |
|
||||
| Live change outlines during a run | tldraw (streamed), Cursor inline diff | after the run only | draw live |
|
||||
| Per-run timeline + revert one | Cursor checkpoints | yes (runs.rs) | per-client runs; fix stale card |
|
||||
| Variations side by side | Stitch | none | duplicate + keep one |
|
||||
| Comments batched into one prompt | v0 annotations | feedback + annotations exist | "Send open notes to chat" |
|
||||
| Voice | Cursor, Stitch | none | P2 |
|
||||
|
||||
## 4. Conclusions
|
||||
|
||||
1. The best tools send **identity + pixels + region**. PaperCraft sends identity only. Adding a screenshot crop to chips and an area pick is the biggest lever for "point at the canvas".
|
||||
2. Split actions into **two lanes**. Deterministic actions (lint autofix, bind hard-coded colors to the nearest variable, swap copies for instances, rename default layers) run in-process through `runs::Tracker::call`, so they land as a reviewable run with no model. Intent actions (Optimize layout, Variations, Make consistent) send a scoped prompt from a template.
|
||||
3. A DS in context is necessary but not enough (the Figma Make complaints). Pair the digest with a **reuse check**: lint "looks like component X but isn't an instance".
|
||||
4. Presence beats a pill. Outline the nodes the current run touched, live, and say where the agent is (page, frame).
|
||||
5. Ghost previews in egui are costly (a second document render path). The cheaper equivalent already exists: apply live, show the run outlines, Accept/Revert per run or per change. Variations give "preview" by construction: siblings on the canvas, keep one.
|
||||
@@ -0,0 +1,124 @@
|
||||
# Chat ↔ canvas: spec
|
||||
|
||||
What and why for (a) the gaps in `gaps-found.md`, (b) a chat that acts and points at the canvas, and (c) chat grounded in the project's design system. Research and the current-state map are in `research.md`. Cost: **S** < 1 day, **M** 1–3 days, **L** > 3 days. Everything here uses egui primitives already in the tree (Area overlays, painter, popups) and no new crates.
|
||||
|
||||
## Principles
|
||||
|
||||
* **Two lanes.** Deterministic actions run in-process with no model. Intent actions send a scoped prompt. Both go through `runs::Tracker::call`, so each lands as one reviewable, revertable run.
|
||||
* **Identity + pixels + region.** What the user points at goes to the agent as node ids, a crop image, and a world rect.
|
||||
* **Reuse before draw.** The agent sees the design system every turn, and a lint flags work that should have been an instance or a token.
|
||||
* **Never steal the camera.** Agent camera moves obey the existing `SELF_FOLLOW_QUIET` rule (`src/ui/chat.rs`).
|
||||
|
||||
## P0
|
||||
|
||||
### P0.1 Annotations in screenshots (gap 1) — S/M
|
||||
`get_screenshot {annotations?: bool}`. When true, the image includes note cards, badges and leader lines for notes whose target is inside the captured roots.
|
||||
* How: clone the page doc, add each card as temporary frame + text nodes at `annotation_layout` placement (`card_size`, the placement and routing functions), add leader lines as path nodes, then call the existing `io::png::render`. Captured bounds grow to include the cards. No second renderer.
|
||||
* Accept: a test renders a frame with 2 notes, and the PNG bounds include both cards. With `false` or omitted, the output is byte-identical to today. `docs/annotations.md` and `skills/papercraft/references/tools.md` are updated.
|
||||
|
||||
### P0.2 Viewport: read, set, switch page (gaps 2, 3) — S
|
||||
* `get_basic_info` and `get_selection` gain `viewport: {pageId, rect:[x,y,w,h] world, zoom}` from `ed.camera` and `ed.viewport`.
|
||||
* New tool `set_viewport {pageId?, nodeIds? | rect?, zoom?, padding?}`. It switches the active page when `pageId` differs (as the page list does) and fits the nodes or rect. `zoom` sets an explicit level within the canvas's min/max, instead of the 1.0 cap in `select_nodes`. It is refused with `"user is moving the canvas"` when the camera moved within `SELF_FOLLOW_QUIET`, unless `force:true`.
|
||||
* `select_nodes` keeps its behaviour. Its error on another page points to `set_viewport`.
|
||||
* Accept: tests for the page switch, fit, explicit zoom and the quiet refusal. The tool count in `docs/agents.md` is updated.
|
||||
|
||||
### P0.3 Runs per agent client + per-run review (gap 7, run-card follow-ups) — M
|
||||
* `PaperServer::call_tool` reads `context.peer.peer_info()` (rmcp `clientInfo.name`/`title`) and the session id, and passes `client` on `Call`. Chat turns pass their chat id. `Tracker` keeps one open run per client key, instead of one global `open`. `Run` gains `client: String` (serde default `""`).
|
||||
* Agent view and chat: each run shows its client ("Claude Code · subagent 2"). Revert of one run is already in `runs::revert`; refuse it when a later run touched the same nodes, and offer "Restore to here" (`revert_all`).
|
||||
* Fix the run card going stale after undo (refresh `ReviewDiff` on `history.generation()` change). The "Working" pill clears on turn end. Stop shows as "Stopped", not an error.
|
||||
* Accept: two MCP sessions editing at once give two runs with the right counts. Reverting one leaves the other's nodes. The `tests/mcp.rs` and `runs.rs` tests pass.
|
||||
|
||||
### P0.4 Design-system context in every chat turn (user request) — M
|
||||
Today `system_prompt()` (`src/ui/chat.rs:602`) is the skill text plus the project name. Add a **DS digest**, built in-process from data the tools already expose:
|
||||
* **Variables/tokens**: collection → group → names, with modes. Values only for colors, listed as `name=#hex/#hex(dark)`. Tokens (space, radius, type) as scales. Source: same as `get_variables`.
|
||||
* **Components**: page mains and enabled-library mains (`get_components`, `ed.library_docs`), as `componentKey · name · w×h · uses`. Sorted by uses, then name.
|
||||
* **Libraries**: enabled libraries with their update status (`libraries::status`).
|
||||
* **Annotation template**: the active template's enabled types (`get_annotation_template`).
|
||||
* **Rules line**: "Reuse: use `create_instance`/`swap_instance` for anything matching a component; colors must be `var(--…)`; spacing on tokens."
|
||||
|
||||
Budget and caching:
|
||||
* Cap the digest at ~1,500 tokens (≈6 KB, a named constant). When over the cap, keep the top components by use, collapse variable groups to counts, and append "call get_components / get_variables for the rest".
|
||||
* Cache it on `ChatState`, keyed by a hash of (variables, tokens, component keys + names, enabled library versions, template id). Rebuild only on a key change.
|
||||
* API backend: the digest goes in the system prompt, in its own cache-breakpoint block after the skill (Claude host). CLI/ACP backends: they get the system prompt once per session, so when the hash changes between turns, prepend `[Design system changed] <digest>` to the next user message.
|
||||
* Settings → Chat gets a toggle, "Send design-system summary" (default on), because it costs tokens.
|
||||
|
||||
**Reuse check** (the Figma Make failure mode):
|
||||
* New lint rule `not-an-instance`: a frame whose structure matches a component main (same child kinds/count, size within 4 px, same text roles) but that is not an instance. Its fix is "swap_instance with componentKey X". Computed with the same structure walk `diff_frames` uses.
|
||||
* `lint_design {fix:true}` does **not** auto-swap; swapping is a user action (P0.5).
|
||||
|
||||
Accept: a unit test of the digest for a fixture project, checking it is under the cap with top-N kept. A test that the hash is unchanged when unrelated nodes move. A lint test where a hand-made copy of a button is flagged and an instance is not. The context meter shows the added tokens.
|
||||
|
||||
### P0.5 Scoped actions on a selection: "Optimize" menu (user request) — M
|
||||
An **Actions** menu on the selection. It opens from the canvas right-click menu (extend `canvas_menu` in `src/ui/annotations.rs:438`, which today has only "Annotate"), from an "Actions" chip next to `@ Selection` in the composer, and from the command palette (`src/ui/palette.rs`). It works in Design and Chat modes.
|
||||
|
||||
Deterministic lane (in-process, no model, one run each, instant):
|
||||
| Action | Runs | Exists |
|
||||
|---|---|---|
|
||||
| Fix lint here | `lint_design {nodeId, fix:true}` | yes |
|
||||
| Bind to tokens | for each hard-coded color, space or radius in the subtree, `bind_variable` to the nearest variable/token within a tolerance; the rest are listed in the result | bind exists; nearest-match is new (S) |
|
||||
| Use components | for each `not-an-instance` hit, `swap_instance` | needs P0.4 lint |
|
||||
| Rename layers | `rename_nodes` for default names (`Frame 12`, `Text`) from text content or role | new heuristic (S) |
|
||||
| Annotate | opens the existing popover | yes |
|
||||
|
||||
Intent lane (sends a prompt to chat with a selection chip and crop; switches to Chat mode like "Annotate with AI" does, but **sends**, because the action is explicit):
|
||||
* **Optimize**: "Tighten layout and spacing to the token scale, fix contrast to AA, keep the content. Prefer existing components."
|
||||
* **Make consistent with DS**: "Replace hand-made parts with instances of the listed components, bind every color and space to variables."
|
||||
* **Annotate with AI** (existing), **Write states** (empty, loading, error), **Variations ×3** (P1.3).
|
||||
Prompts live in one const table in `src/ui/chat_msgs.rs`, next to `SUGGESTIONS`.
|
||||
|
||||
Review: every action, in either lane, ends as a run, so the existing review overlay, Run card and Revert/Accept apply. A deterministic action shows a toast "Bound 14 colors · Review · Revert".
|
||||
|
||||
Accept: each deterministic action has a test on a fixture (counts, one undo step, one run). The intent actions put the right prompt and chips into a sent message (pattern: `ai_prompt_and_chat_hand_off_do_not_send`).
|
||||
|
||||
### P0.6 CLI/app version check (gap 4) — S
|
||||
* `papercraft-cli call`/`shell` compares its own `CARGO_PKG_VERSION` with the app's `serverInfo.version` (`src/mcp/server.rs:39`). On mismatch it prints once to stderr: `papercraft-cli 0.6.1 ≠ app 0.6.2; reinstall the CLI`.
|
||||
* `list`/`schema` use the app's `tools/list` when the app is running and fall back to the built-in list offline, so new tools show even from a stale CLI.
|
||||
* Accept: a test with a fake server version prints the warning. `list` against a running app includes a tool absent from the built-in list.
|
||||
|
||||
## P1
|
||||
|
||||
### P1.1 Point at the canvas from chat: pick and area — M
|
||||
* The composer gets **Pick** (crosshair; click a layer or Shift+drag an area; Esc cancels) next to `@ Selection`. It does not change the user's selection.
|
||||
* `SelChip` (`src/ui/chat.rs:201`) gains `rect: Option<[f64;4]>` (world) and `image: Option<path>`. An area chip is sent as `[Area] page P, rect x,y,w,h, contains: <ids…>` plus the crop image. Layer chips also attach a crop.
|
||||
* Crop: `get_screenshot`'s render of the intersecting roots, clipped to the rect (S; add a `clip` rect to `io::png::render`), with annotations per P0.1. Images go through the existing attachment path (image blocks for API; `.papercraft-chat-tmp/` paths for CLI/ACP).
|
||||
* ⌥-click on the canvas in Chat mode adds a layer chip directly (Cursor pattern). Chips are inserted at the composer cursor.
|
||||
* Accept: an area chip lists the contained node ids and attaches a PNG. Several picks give several chips.
|
||||
|
||||
### P1.2 Agent presence and live outlines — M
|
||||
* While a run is open, outline the nodes from the last N calls' `follow_node` keys (`chat_msgs::follow_node`) on the canvas in the accent color, fading over 3 s. Label the newest outline with the verb (`agent_verb`) and the client name: an **agent cursor**. Draw it as one `egui::Area` like `review_overlay`, on any page; a chip "Agent on page X →" shows when the agent works on another page.
|
||||
* This works for MCP clients too (not only chat), using the per-client run from P0.3.
|
||||
* After the turn: today's review overlay. During the turn: cheap touched-outlines only (no full diff per call).
|
||||
* Accept: a UI test shows the overlay present while a run is open and gone 3 s after the last call. There is no repaint loop when idle (respect `AGENT_PILL_FRAME_MS`).
|
||||
|
||||
### P1.3 Variations side by side — S/M
|
||||
* Action "Variations ×3" duplicates the frame 3 times to the right (`duplicate_nodes`, offset by width + 80), names them `<Frame> · Variant A/B/C`, and sends the agent one prompt per variant with distinct directions (compact, spacious, bold). One run.
|
||||
* Each variant gets an on-canvas tag with **Keep**. Keep deletes the other variants and the original, or replaces the original with the kept one when "replace original" is on. It is one undo step.
|
||||
* Accept: Keep leaves exactly one frame with the original's name and position.
|
||||
|
||||
### P1.4 On-canvas Ask popover — S
|
||||
* ⌘K on a selection (or "Ask…" in the Actions menu) opens a small prompt box anchored above the selection, using the annotation popover's placement. Enter sends to chat with the chip and crop, Chat mode stays as it is, and the reply streams in the chat column. This gives the Stitch/v0 "type on the element" feel without leaving Design mode.
|
||||
* Accept: sending from the popover yields the same message as chip + composer.
|
||||
|
||||
### P1.5 Progress for long batches (gap 6) — S
|
||||
* The run tracker exposes `calls` and `added/changed/removed` live. The pill shows `Styling · 212 calls · 1,340 changes` for any client, and the chat's step group shows the count while running.
|
||||
|
||||
### P1.6 Notes to prompt — S
|
||||
* "Send open notes to chat" on a frame: open feedback + annotations of type `todo`/`question` become one batched prompt with a chip (the v0 annotations pattern). It reuses `selection_notes`.
|
||||
|
||||
## P2
|
||||
|
||||
* **True ghost preview** (L): apply agent edits to a shadow `Document` and draw it translucent over the canvas with Accept/Reject before commit. Costly: every tool would have to target the shadow doc and the renderer composites two docs. P1.2 + P1.3 + run revert cover most of the value; revisit only if users ask.
|
||||
* **Dev reload** (gap 5, M): `papercraft --reopen` saves, re-execs the new binary and reopens the project and page. It is a menu item in debug builds.
|
||||
* **Chat modes** (S): "Critique" (read-only tools only, returns a list of issues as annotations) vs "Build". It filters `tool_specs()` per mode, as tldraw does.
|
||||
* **Sketch to UI** (M): draw on a whiteboard page, then "Make real" sends the crop with the DS digest and the prompt to build a frame next to it.
|
||||
* **Voice** (M): push-to-talk into the composer through the OS dictation API. It is out of scope until the other items ship.
|
||||
* **Docs** (S, gap 9): document `get_tree_summary` depth with an example; consider `depth` counting from the artboard.
|
||||
|
||||
## Out of scope
|
||||
|
||||
* New model backends, in-app agent install, web build chat.
|
||||
* Gap 8 (fixed). The 0.6.2 platform items (GPU panic, Wayland clipboard, email ellipsis) are tracked in their own specs.
|
||||
|
||||
## Order
|
||||
|
||||
P0.2, P0.1, P0.6 (small, unblock agents), then P0.4 (digest + lint), then P0.5 (actions, needs the lint), then P0.3. After that P1.1 and P1.2, then the rest.
|
||||
@@ -0,0 +1,19 @@
|
||||
# Tasks
|
||||
|
||||
Spec: `spec.md`. Stop on failure. One shared `target/`; never install or quit the user's PaperCraft (design agents are using it); test builds use their own `PAPERCRAFT_HOME` / `PAPERCRAFT_MCP_PORT` and are killed by PID. Each agent runs `cargo test --release` and `cargo clippy --release --all-targets` clean before reporting, and deletes its temp homes.
|
||||
|
||||
## Wave A (parallel, disjoint files)
|
||||
|
||||
- [x] A1. **MCP for agents**: P0.2 viewport (`get_basic_info`/`get_selection` viewport, `set_viewport`), P0.1 `get_screenshot {annotations}`, P0.6 CLI version check + live `list`/`schema`, `annotate {tidy}` returns `{tidied: n}` when no notes are given, `get_tree_summary` depth doc (P2 docs). Files: `src/mcp/exec.rs`, `src/mcp/tools.rs`, `src/io/png.rs`, `src/bin/*cli*`, docs. Tests.
|
||||
- [x] A2. **Design-system context**: P0.4 digest (new module, cached on ChatState, cap const, system prompt block, CLI/ACP "[Design system changed]" prefix, Settings toggle, context meter) + `not-an-instance` lint rule in `src/mcp/lint.rs`. Tests.
|
||||
- [x] A3. **Runs per client**: P0.3 (`src/runs.rs`, `src/mcp/server.rs` client name, agent view/run card labels, per-run revert guard, stale card after undo, Working pill clears, Stop not an error). Tests.
|
||||
|
||||
## Wave B (after A)
|
||||
|
||||
- [x] B1. **Actions menu**: P0.5 both lanes + toast, prompts table in `chat_msgs.rs`.
|
||||
- [x] B2. **Pointing**: P1.1 Pick/area chips with crops, ⌥-click, P1.4 ⌘K Ask popover.
|
||||
- [x] B3. **Presence**: P1.2 agent cursor + live outlines, P1.5 live counts in pill, P1.6 notes to prompt.
|
||||
|
||||
## Wave C
|
||||
|
||||
- [ ] C1. Review (read-only) against spec, fixes (not done yet); docs (done) (`docs/chat.md`, `docs/agents.md`, README tool count); install once design agents are done; live check.
|
||||
@@ -0,0 +1,86 @@
|
||||
# Chat: fewer tokens, model and effort, image zoom
|
||||
|
||||
## Why
|
||||
|
||||
A Claude Code chat turn in the Interview project cost $0.56 (API-equivalent). Most of it was
|
||||
a 50k-token prompt prefix written to the cache and re-read on every tool step. Measured with
|
||||
`claude -p --model haiku` and the chat's own flags, before anything the user typed:
|
||||
|
||||
| Claude Code flags | Prompt tokens |
|
||||
|---|---|
|
||||
| what the chat sent | 42.7k |
|
||||
| `--disable-slash-commands` (no user skills list) | 31.4k |
|
||||
| `--setting-sources project,local` (no user CLAUDE.md, hooks, plugins) | 30.6k |
|
||||
| both + `--tools Read,TodoWrite,ToolSearch` | 20.6k |
|
||||
|
||||
`--tools` without `ToolSearch` makes it worse (43.9k): the CLI then loads every PaperCraft
|
||||
tool schema up front. Bash, Edit, Write and the web tools were never usable anyway: in `-p`
|
||||
mode only `mcp__papercraft__*` is allowed, everything that needs permission is denied.
|
||||
|
||||
The API backend sends all 70 tool schemas (about 77 KB, ~19k tokens) on every request.
|
||||
|
||||
## What
|
||||
|
||||
1. **Lean Claude Code sessions** (Settings → Chat, on by default): the flags above.
|
||||
Off keeps the user's own CLAUDE.md, hooks, plugins and skills.
|
||||
2. **API tool search**: on `api.anthropic.com`, the tools a design turn rarely needs are sent
|
||||
with `defer_loading` behind the BM25 tool-search tool; the core set stays loaded.
|
||||
3. **Ask before building**: the chat system prompt tells the agent to ask up to three short
|
||||
questions (with proposed defaults) when a request is too broad to build well, and to
|
||||
avoid screenshots it does not need.
|
||||
4. **Model and effort for Claude Code**: the agent panel's Model/Effort block, today only for
|
||||
Claude API endpoints, also shows for Claude Code. Claude Code no longer gets the default
|
||||
API endpoint's model forced on it (a non-Claude default endpoint sent its model id).
|
||||
5. **Images: smaller to send, zoom for detail**: attachments are sent at most 1568 px on the
|
||||
long edge (about 1.15 MP, at most ~1.6k image tokens instead of ~4.8k). When an image was
|
||||
scaled down, the original is saved next to the sent one; the `zoom_image` tool returns a
|
||||
region of it at full resolution. It reads only files under the project's chat folder.
|
||||
|
||||
## Acceptance
|
||||
|
||||
* Claude Code args contain the lean flags when the setting is on, and none of them when off.
|
||||
* API request: core tools have no `defer_loading`, the others do, and the tool-search tool is
|
||||
present; non-Claude hosts get the plain full list.
|
||||
* A Claude Code chat with a picked model/effort passes `--model`/`--effort`; without a pick it
|
||||
passes neither.
|
||||
* A 4000×1000 PNG is sent as 1568×392 and its original is kept; `zoom_image` on the sent path
|
||||
returns a crop of the original; a path outside the chat folder is refused.
|
||||
|
||||
## Follow-up: brief, design system, finish (after the binder test)
|
||||
|
||||
The binder page (Claude Code, lean) built straight away at 1440x1024 with 44px buttons
|
||||
everywhere, no components for the 9 repeated slots, and a 350-word report. The 62.8k context
|
||||
was a 20.3k prefix (lean, as measured) plus the agent's own 19.3k tokens of HTML and the tool
|
||||
results; each part was written to the cache once. Lean mode also dropped the user's
|
||||
`"model": "opus"` from `~/.claude/settings.json`, so the turn ran on Sonnet 5.5.
|
||||
|
||||
6. **`ask_user` tool + question card**: one to five questions, two to five options, palette
|
||||
options with swatches; the chat draws clickable chips, "Send answers" and "Use your
|
||||
defaults". The tool only checks the shape and tells the agent to end its turn.
|
||||
7. **Chat rules**: a brief before the first design (frame size, density, palette, type, scope,
|
||||
references), variables and components first, sizes from the agreed density, a five-bullet
|
||||
report and next steps through `ask_user`.
|
||||
8. **Lint**: `touch-target` asks 24px (WCAG 2.2 AA) inside screens 1024px or wider, 44px on
|
||||
narrower screens and standalone components; the autofix follows.
|
||||
9. **Lean keeps the user's model**: with no pick, the chat passes the `model` from the user's
|
||||
Claude Code settings.
|
||||
|
||||
## Follow-up: long chats (the Jira chat at 230k)
|
||||
|
||||
The Jira chat reached 230k of context; every step re-read it. Claude Code's `total_cost_usd`
|
||||
adds up over a resumed session, so turns showed the running total ($6.72 for a $0.47 turn).
|
||||
|
||||
10. **Meter**: a 1M window (current Claude models), counts as `1.6M`; amber past 150k.
|
||||
11. **Per-turn cost**: `Message::session_cost` keeps the CLI's running total; a resumed turn shows
|
||||
the difference (older saved turns are derived at display time).
|
||||
12. **Fresh session past 150k (now 120k)** (CLI backends): not resumed; the new session starts with a recap
|
||||
(the last 8 requests, the last report) and reads the canvas with tools.
|
||||
13. **Compact now**: clicking the meter queues the same fresh start for the next message.
|
||||
14. **API context editing**: `clear_tool_uses_20250919` past 60k input, keep 4, clear ≥15k, tool
|
||||
inputs too (beta `context-management-2025-06-27`).
|
||||
15. **Research sub-agent**: with Web on, Claude Code gets a `researcher` agent on Haiku
|
||||
(`--agents`); it searches and fetches, the main context gets the kept paths only. A research
|
||||
turn on a fresh chat: $0.26 in all, main context 25.8k (the late in-session one cost $1.71).
|
||||
Sub-agent text stays out of the chat; its tool calls (and galleries) show.
|
||||
16. **Turn budget** (Settings → Chat, default 10M tokens read): a turn past it is stopped like
|
||||
Stop, with a note.
|
||||
@@ -0,0 +1,46 @@
|
||||
# Component properties
|
||||
|
||||
## Why
|
||||
|
||||
A status badge, a tab bar or a list row is drawn once as a component, but every use differs:
|
||||
the status is Autorizada, Rejeitada or Na SEFAZ; a different tab is active; the label and the
|
||||
icon change. Today each case is its own component (`Badge / Green`, `Tabs / Lojas`, `Tabs /
|
||||
Pedidos`) or hand edits on layers, so a design needs a component per state and an agent edits
|
||||
layer by layer.
|
||||
|
||||
## What
|
||||
|
||||
1. **Properties live on the main component** (`Node.props`), each with a name and a type:
|
||||
* `text`: the text of a layer (`layer`).
|
||||
* `bool`: shows or hides a layer.
|
||||
* `icon`: the icon of an icon layer (`Icon / set:name`), any `set:name`.
|
||||
* `enum`: a list of options; each option is a set of changes in the `overrides` format
|
||||
(`{"Label": "Rejeitada", "$root": {"background": "var(--danger-bg)"}, "Dot": {"icon": "lucide:x"}}`),
|
||||
so an option can restyle, retext, show, hide or re-icon any layer. Fully custom.
|
||||
2. **Instances keep their values** (`Node.prop_values` on the instance root) and apply them as
|
||||
overrides, so edits to the main keep them; editing the definitions re-applies every
|
||||
instance's values on every page.
|
||||
3. **Templates** make the common cases one call: `text` (a text property per text layer),
|
||||
`active` (an enum choosing which item of a row, tab bar or nav is active, with active and
|
||||
inactive styles) and `status` (an enum of statuses, each a colour that tints the root and
|
||||
colours its text and icon).
|
||||
4. **Where to use it**: `set_component_props` (define, or from a template),
|
||||
`set_instance_props` (many instances in one call), `get_components` (lists properties),
|
||||
`<x-paper-instance component="Badge" props='{"Status":"Rejeitada"}'/>` in `write_html`,
|
||||
the inspector (a control per property on an instance; templates on a main), and code
|
||||
links (`{Status}` in a usage template is the instance's value).
|
||||
|
||||
## Not now
|
||||
|
||||
Variant sets made of several mains, nested property exposure (a row exposing its badge's
|
||||
properties), numeric properties.
|
||||
|
||||
## Acceptance
|
||||
|
||||
* A badge with an enum Status (Autorizada, Rejeitada) shows the option's colours and label on
|
||||
each instance; switching the value updates only that instance.
|
||||
* Editing the main (gap) keeps every instance's values; editing an option's colour updates
|
||||
every instance using it.
|
||||
* The `active` template on a five-item tab bar makes one property whose options are the item
|
||||
names; each instance shows its active item highlighted.
|
||||
* An icon property swaps the icon and survives a sync of the main.
|
||||
@@ -0,0 +1,44 @@
|
||||
# Package A report: Interview Design System
|
||||
|
||||
Date 2026-10-23. Project `Interview` (fileName verified). Backup: `/Volumes/Storage/Projects/Interview.backup-20261023-0112`. Screenshots in `A/` (before-*, after-*). Mapping files: `variable-mapping.csv`, `component-mapping.csv` (old name, new name, componentKey, action; 280 rows).
|
||||
|
||||
## 1. Variables (collection `Colors`)
|
||||
- 161 -> 137 variables (25 merged, +1 `color/brand/primary-strong`). All names are `color/<group>/<name>` (csv `new` column with `color/` prefix). SYS-03: named by role, not value.
|
||||
- Rename = create new + repoint every bound fill (`update_styles`) + delete old. The app has no rename tool, and `delete_variables` detaches fills, so repointing was required. 4293+ bound fills repointed on all 3 pages. No dangling `var(--old)` found afterwards.
|
||||
- Merges (csv "merge" rows) repointed to the canonical variable then deleted (no leftover aliases needed because nothing binds them).
|
||||
- SYS-02 (semantic alias over primitive value): `color/brand/pressed` and `color/text/link` are aliases of `color/brand/primary-strong` (#0958d9); `color/status/info` is an alias of `color/brand/primary`.
|
||||
- Contrast: `#1677ff` + white = 4.10:1 FAIL, `#0958d9` + white = 6.16:1 pass (`playbook contrast`), `#0958d9` on `#e6f4ff` 5.50:1 pass (SYS-05). 40 DS fills (Button / Primary x3, Avatar x5, sidebar active items, dots, brand marks, OK button) rebound to `primary-strong`; 16 literal `#1677ff` text nodes now use `text/link` colour. DS contrast errors 128 -> 67 on the original components.
|
||||
- Final groups (Colors): avatar 5, bg 6, border 4, brand 6 (primary, primary-strong, pressed, hover, secondary, focus-ring), button 1, camera 2, code 2, control 1, diff 2, hero 2, ide 31, status 9, sidebar 8, syntax 7, syscheck 6, tag 18 (tag/<hue>/{bg,border,text}), text 6, whiteboard 9, auth 12.
|
||||
- Colours the page still uses as hard-coded literals (text colours, borders) are not bound; that is B/C work (tools bind fills only).
|
||||
|
||||
## 2. Tokens
|
||||
- Old `Tokens` collection (32: 26 number, 3 shadow strings, 4 z-index strings) deleted; shadows and z-index dropped (unsupported in PaperCraft, unreferenced). Empty `Tokens` collection remains (no delete-collection tool; remove in the Variables modal).
|
||||
- New collection `Spacing & Size` (34 Number variables): `space/{4,8,12,16,24,32,48,64}` (12 added), `radius/{sm4,md8,lg12,xl16,pill999}`, `font/size/{xs12,sm14,md16,lg20,xl24,2xl32,3xl40,4xl48}` (18 dropped), `font/leading/{xs16..2xl40}`, `layout/page-padding(-lg)`, `layout/page-width/{xs480,sm420,md720,lg900,xl1100}` (3 duplicate widths removed). SYS-01/SYS-02.
|
||||
- Nothing is bound to them yet (tokens are substituted when written). Use `bind_variable` in later work.
|
||||
|
||||
## 3. Components (Design System page: 211 -> 260 mains)
|
||||
- Consolidated: Ant Button Primary/Default/Danger (9), Ant Tag (5), Ant Segmented (3), Ant Table (2) dropped after `swap_instance` of their DS cells to the kept set (19 swaps, 19 mains and 19 cells deleted). Ant Text / Text Danger kept as `Button / Text` and `Button / Danger Text` (SM/MD/LG). AntD showcase groups removed, cells moved into `Group / Buttons`.
|
||||
- 78 mains renamed to `Group / Variant [/ SM|LG]` (see csv). Button MD height 34 -> 32 (SM 28, LG 40). `Page Header` kept as `Header / Page` (no action slot; not equal to `Header / Admin`, so not merged).
|
||||
- Fixed: `Header / Candidate` height 0 -> 134 (Header Card height 100% -> fit-content). 8 `Alert / *` 456/478/522 -> 38 high (message text width 0px -> fit-content).
|
||||
- Moved 68 page-local components onto the DS page (17 Admin, 51 Candidate): each moved with `move_nodes toPageId`, instances on the source page re-pointed with `swap_instance` by componentKey, instance layer names updated. Packed at x=1560, y 8623..13765 on the DS page. `Candidate Message / Success` renamed `Banner / Success` (name clash with the Admin one, different design).
|
||||
- Skipped (not moved): `T Hdr`, `T Donut`, `T Spark`, `T Stat` (Admin template stubs, 0 uses; equivalents exist in DS: Section Title, Donut, Sparkline, Stat). Delete them in package B.
|
||||
- Not done: collapse of combinatorial variants (Segmented Active N, Tabs Bar N, 11 sidebars, content-baked Challenge Option/Timeline, colour-named Stat/Sparkline), missing states (hover/disabled etc.), Input/Select/Textarea LG state matrix (SYS-06/07 follow-ups), `Light` mode rename of `Default` (no tool).
|
||||
- Side effect: while moving, Admin's own copies of `Button / Primary Small`, `Button / Primary Large`, `Button / Default` were moved by mistake (the copy filter used renamed names) and then deleted from DS; no Admin instance pointed at them (nested ones live inside other instances). B should re-import or swap to the DS mains below.
|
||||
- Admin/Candidate still carry their old copies of the other 211 DS components under old names; B/C: use `component-mapping.csv` and `swap_instance componentKey`.
|
||||
|
||||
### Kept component keys (packages B and C)
|
||||
`componentKey` for every kept or moved main is in `component-mapping.csv` (column 3). Frequently needed:
|
||||
Button / Primary `64e1d4aaf56fe6a4:5980`, Primary / SM `:5982`, Primary / LG `:5984`, Secondary `:5986`, Secondary / SM `:5988`, Secondary / LG `:5990`, Ghost `:5992`, Danger `:5998`, Dark `:6004`, Icon / Bell `:6010`.
|
||||
Header / Admin `64e1d4aaf56fe6a4:6480`, Header / Page `:6476`, Header / Candidate `:6489`, Header / Admin / With Action `1c5e5918`.
|
||||
Form Field / Default `:6663`, Card / Default `:6054`, Alert / Error `:6728`, Input / Default `:6575`, Sidebar / Admin `:6906`, Sidebar / Candidate `:7370`.
|
||||
(Key prefix is the DS page id `64e1d4aaf56fe6a4`.) Moved components have short keys (e.g. `bd7f34e5`), see csv.
|
||||
|
||||
## 4. Annotations
|
||||
Template `Dev handoff` set on the DS page. 11 notes (dev, a11y, behaviour): one Primary per screen and contrast pair, tag colour by status, Data Table single set, Segmented, Input states, Alert, Tab hit area, Form Field, Header variants, Pill.
|
||||
|
||||
## 5. Lint (DS page, byRule)
|
||||
Before: contrast 128, hard-coded 11, layer-name 7, spacing 57, touch 16, type-scale 42 (list truncated at 200; counts exact).
|
||||
After (whole page): contrast 194, hard-coded 366, layer-name 49, spacing 180, touch 30, type 135. The rise is the 68 moved local components (contrast 127, hard-coded 225, layer-name 42, spacing 123, touch 9, type 93). The original components: contrast 67 (-61), hard-coded 146 (more literals now match a single variable after merges, so lint flags them), layer-name 7, spacing 57, touch 21 (Button MD 32 < 44, desktop density accepted, documented in annotation), type-scale 42. Remaining contrast: `#52c41a`/white 2.3, `#8c8c8c` 3.4 (placeholders), `#bfbfbf` 1.8 (disabled), tag text pairs, IDE greys: design decisions left for follow-up. `lint-ds-before.json` saved in `A/`.
|
||||
|
||||
## Leftovers for the user
|
||||
Empty `Tokens` collection; mode `Default` not renamed; 15 `Label / *` stubs at x=30000 not deleted; Colors board has no swatch for `brand/primary-strong`; `brand/hover` (#4096ff) with white text fails contrast (hover state unused in DS).
|
||||
|
After Width: | Height: | Size: 59 KiB |
|
After Width: | Height: | Size: 75 KiB |
|
After Width: | Height: | Size: 464 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 254 KiB |
|
After Width: | Height: | Size: 454 KiB |
|
After Width: | Height: | Size: 109 KiB |
|
After Width: | Height: | Size: 58 KiB |
|
After Width: | Height: | Size: 254 KiB |
@@ -0,0 +1,35 @@
|
||||
# Package A2 report: Design System page cleanup
|
||||
|
||||
Date 2026-10-23. Project `Interview` (fileName verified). Page `Design System` only. Evidence in `A2/`: `issues-before.json` (all 959 issues, linted per top-level frame to avoid the 200 cap), `issues-7.json` (final), `after-brand-swatch.png`, `after-components.png`. Backup: `/Volumes/Storage/Projects/Interview.backup-20261023-0112`.
|
||||
|
||||
## Lint counts (DS page, byRule, exact)
|
||||
| rule | before A2 | after A2 | pre-A baseline |
|
||||
|---|---|---|---|
|
||||
| contrast (errors) | 194 | **0** | 128 (67 on original components) |
|
||||
| hard-coded-color | 366 | **7** | 11 |
|
||||
| spacing-grid | 180 | **0** | 57 |
|
||||
| touch-target | 30 | 30 (accepted, see below) | 16 |
|
||||
| type-scale (info) | 135 | 135 (not in scope) | 42 |
|
||||
| layer-name | 49 | 49 (not in scope) | 7 |
|
||||
Goal met: contrast 0 (target <= 67).
|
||||
|
||||
## What changed
|
||||
- Hard-coded colours: 359 nodes bound by exact hex match (352 text colours, 12 fills) via `update_styles` `var(--color-...)`. White text bound to `color/text/inverse`, not `bg/surface` (same hex, text role). Note: `bind_variable text-color` was not recognised by lint, `update_styles color:var()` was; use that.
|
||||
- Contrast (SYS-05, A11Y checked with `playbook contrast` maths, all pairs >= 4.5):
|
||||
- white on `#1677ff`/`#4096ff` -> parent fills rebound to `color/brand/primary-strong` (#0958d9, 6.16); 16 fills + 5 matching 1px strokes bound.
|
||||
- white on `#52c41a` (Score Dots) -> `color/status/success-text` (#237804, 5.59); white on `#ff4d4f` -> `color/status/error` (5.57).
|
||||
- `#5a5a5a` on `#1a1a1a` -> `color/ide/text-muted` (4.91); `#888`/`#666` on `#252526|#2a2a2a|#252525|#181818|#161616` -> `color/ide/text-dim` (>= 7.5); `#5a80a8` on `#1c2a40` -> `ide/text-dim`; `#13a8a8` on `#1a3540` -> `syntax/type` (6.33); sidebar `#64676c` -> `sidebar/text-muted` (4.51).
|
||||
- `#8c8c8c`/`#bfbfbf`/`#bbb`/`#878787`/`#848484` on light -> `color/text/secondary` (6.7-7.0). Note placeholders and disabled input text now read as secondary text; disabled look is carried by the grey fill only.
|
||||
- `#52c41a`, `#16a34a` text on light -> `status/success-text`; white on `#faad14`/`#13a8a8` (Score Dots 2, Theory Next) -> `color/text/primary`.
|
||||
- Variable VALUES changed (global, affects Admin and Candidate too, all pairs now pass): `color/tag/gold/text` #ad6800 -> #874d00, `color/tag/green/text` #389e0d -> #237804, `color/tag/cyan/text` #08979c -> #006d75. Bound fills followed automatically.
|
||||
- `Pipeline Row / Dark` was a transparent component meant for a dark card: gave it `color/ide/card` background, stage text `ide/text-muted`, name `text/inverse`.
|
||||
- Spacing: all 180 off-grid nodes bound to `space/*` (nearest in-scale value: 1,2,3,5->4; 6,7,9->8; 10,11->12; 14,18->16; 22->24; gap 6->8). On-grid padding was not rebound (would be thousands of bindings with no lint effect). Visual check of buttons, tags, rows, toolbars: nothing broke.
|
||||
- Radii: all uniform radii bound to `radius/*`: 289 -> sm (3,4,5), 442 -> md (6,7,8), 75 -> lg (12), 153 -> pill (99,999). Multi-value radii (per-corner) not touched; no radii above 12 other than the pill ones existed.
|
||||
- Colors board: added swatch `color-brand-primary-strong` (#0958d9) in the Brand card.
|
||||
- `Label / *` stubs: they are plain label frames, not components (`get_components` lists none), and each captions a real component at x=30000. Deleted only the 5 whose component has 0 instances (`Link Field / Copied`, `Challenge Option / Two Sum Off`, `... / URL shortener On`, `... / JavaScript closures Off`, `Timeline / Step 3`). The other 10 kept because their component has instances. Delete them too if you read the instruction as "all 15".
|
||||
|
||||
## Skipped / accepted
|
||||
- 7 hard-coded: `#000000` text on `Stars / 0..5` (6, no exact variable; `text/primary` is #000000e0, near match not in the audit mapping) and `#bae0ff` on `Funnel Row` (1, no variable).
|
||||
- Touch targets (30): `Button *` SM 24-28, MD 32-34, LG 40, `Button / Undo|Clear|Delete` 24, bell 36. Accepted desktop density: playbook A11Y-09 (24 px AA minimum, 44 default on touch only) and PAT-06 (about 27-30 px practical floor for dense content). All >= 24 px. Annotation on the DS page already documents it. Candidate pages are desktop IDE flows.
|
||||
- Untouched (out of scope): type-scale 135, layer-name 49, `brand/hover` (#4096ff) with white text (state unused).
|
||||
- Side effect to check: `set_variables` (tag text values) made lint report hard-coded text again on untouched nodes (text bindings seemed to be flattened for some nodes on the DS page); rebinding fixed it. Admin/Candidate pages were not inspected for this; if B/C see text colours turn literal, that is the cause.
|
||||