Hell yeah!
This commit is contained in:
parent
744a92e446
commit
eb4a1fac14
75
bun.lock
75
bun.lock
|
|
@ -4,12 +4,27 @@
|
|||
"": {
|
||||
"name": "workshop",
|
||||
},
|
||||
"packages/attache": {
|
||||
"name": "attache",
|
||||
"dependencies": {
|
||||
"@workshop/shared": "workspace:*",
|
||||
"hono": "^4.8.0",
|
||||
"nanoid": "^5.1.5",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
"packages/http": {
|
||||
"name": "@workshop/http",
|
||||
"dependencies": {
|
||||
"@workshop/nano-remix": "workspace:*",
|
||||
"@workshop/shared": "workspace:*",
|
||||
"hono": "^4.7.11",
|
||||
"@workshop/todo": "workspace:*",
|
||||
"hono": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
|
|
@ -30,7 +45,7 @@
|
|||
"packages/nano-remix": {
|
||||
"name": "@workshop/nano-remix",
|
||||
"dependencies": {
|
||||
"hono": "^4.7.11",
|
||||
"hono": "catalog:",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
|
|
@ -77,8 +92,8 @@
|
|||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
"packages/text-do": {
|
||||
"name": "text-do",
|
||||
"packages/todo": {
|
||||
"name": "@workshop/todo",
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6.8.1",
|
||||
"@codemirror/language": "^6.11.1",
|
||||
|
|
@ -87,17 +102,21 @@
|
|||
"@lezer/generator": "^1.7.3",
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"hono": "^4.8.0",
|
||||
"hono": "catalog:",
|
||||
"luxon": "^3.6.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"hono": "^4",
|
||||
"typescript": "^5",
|
||||
},
|
||||
},
|
||||
},
|
||||
"catalog": {
|
||||
"hono": "^4.8.0",
|
||||
},
|
||||
"packages": {
|
||||
"@codemirror/commands": ["@codemirror/commands@6.8.1", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.4.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "sha512-KlGVYufHMQzxbdQONiLyGQDUW0itrLZwq3CcY7xpv9ZLRHqzkBSoteocBHtMCoY7/Ci4xhzSrToIeLg7FxHuaw=="],
|
||||
|
||||
|
|
@ -113,11 +132,11 @@
|
|||
|
||||
"@discordjs/formatters": ["@discordjs/formatters@0.6.1", "", { "dependencies": { "discord-api-types": "^0.38.1" } }, "sha512-5cnX+tASiPCqCWtFcFslxBVUaCetB0thvM/JyavhbXInP1HJIEU+Qv/zMrnuwSsX3yWH2lVXNJZeDK3EiP4HHg=="],
|
||||
|
||||
"@discordjs/rest": ["@discordjs/rest@2.5.0", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.1.1", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.1" } }, "sha512-PWhchxTzpn9EV3vvPRpwS0EE2rNYB9pvzDU/eLLW3mByJl0ZHZjHI2/wA8EbH2gRMQV7nu+0FoDF84oiPl8VAQ=="],
|
||||
"@discordjs/rest": ["@discordjs/rest@2.5.1", "", { "dependencies": { "@discordjs/collection": "^2.1.1", "@discordjs/util": "^1.1.1", "@sapphire/async-queue": "^1.5.3", "@sapphire/snowflake": "^3.5.3", "@vladfrangu/async_event_emitter": "^2.4.6", "discord-api-types": "^0.38.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-Tg9840IneBcbrAjcGaQzHUJWFNq1MMWZjTdjJ0WS/89IffaNKc++iOvffucPxQTF/gviO9+9r8kEPea1X5J2Dw=="],
|
||||
|
||||
"@discordjs/util": ["@discordjs/util@1.1.1", "", {}, "sha512-eddz6UnOBEB1oITPinyrB2Pttej49M9FZQY8NxgEvc3tq6ZICZ19m70RsmzRdDHk80O9NoYN/25AqJl8vPVf/g=="],
|
||||
|
||||
"@discordjs/ws": ["@discordjs/ws@1.2.2", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.0", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-dyfq7yn0wO0IYeYOs3z79I6/HumhmKISzFL0Z+007zQJMtAFGtt3AEoq1nuLXtcunUE5YYYQqgKvybXukAK8/w=="],
|
||||
"@discordjs/ws": ["@discordjs/ws@1.2.3", "", { "dependencies": { "@discordjs/collection": "^2.1.0", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.0", "@sapphire/async-queue": "^1.5.2", "@types/ws": "^8.5.10", "@vladfrangu/async_event_emitter": "^2.2.4", "discord-api-types": "^0.38.1", "tslib": "^2.6.2", "ws": "^8.17.0" } }, "sha512-wPlQDxEmlDg5IxhJPuxXr3Vy9AjYq5xCvFWGJyD7w7Np8ZGu+Mc+97LCoEc/+AYCo2IDpKioiH0/c/mj5ZR9Uw=="],
|
||||
|
||||
"@lezer/common": ["@lezer/common@1.2.3", "", {}, "sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA=="],
|
||||
|
||||
|
|
@ -129,7 +148,7 @@
|
|||
|
||||
"@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.2", "", {}, "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="],
|
||||
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.12.3", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-DyVYSOafBvk3/j1Oka4z5BWT8o4AFmoNyZY9pALOm7Lh3GZglR71Co4r4dEUoqDWdDazIZQHBe7J2Nwkg6gHgQ=="],
|
||||
"@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.13.0", "", { "dependencies": { "ajv": "^6.12.6", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "express": "^5.0.1", "express-rate-limit": "^7.5.0", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.23.8", "zod-to-json-schema": "^3.24.1" } }, "sha512-P5FZsXU0kY881F6Hbk9GhsYx02/KgWK1DYf7/tyE/1lcFKhDYPQR9iYjhQXJn+Sg6hQleMo3DB7h7+p4wgp2Lw=="],
|
||||
|
||||
"@openai/agents": ["@openai/agents@0.0.8", "", { "dependencies": { "@openai/agents-core": "0.0.8", "@openai/agents-openai": "0.0.8", "@openai/agents-realtime": "0.0.8", "debug": "^4.4.0", "openai": "^5.0.1" } }, "sha512-HAPP4QM47kWeWw70uxCzr5zjqHuDIvQ8Obx+98J66lcEeIZzMChHN60k5ew8DITScmzDVAVuwdzfAImSyq002w=="],
|
||||
|
||||
|
|
@ -139,7 +158,7 @@
|
|||
|
||||
"@openai/agents-realtime": ["@openai/agents-realtime@0.0.8", "", { "dependencies": { "@openai/agents-core": "0.0.8", "@openai/zod": "npm:zod@^3.25.40", "@types/ws": "^8.18.1", "debug": "^4.4.0", "ws": "^8.18.1" } }, "sha512-f+CxHICIFvCwbMCznop+bz+TTgnFfFpscN+9OTfiU5ITnaohRf+qbyU8PRgQZnSbsxRZyTOgqFoJ+2wWxM5tHA=="],
|
||||
|
||||
"@openai/zod": ["zod@3.25.64", "", {}, "sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g=="],
|
||||
"@openai/zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="],
|
||||
|
||||
"@planetscale/database": ["@planetscale/database@1.19.0", "", {}, "sha512-Tv4jcFUFAFjOWrGSio49H6R2ijALv0ZzVBfJKIdm+kl9X046Fh4LLawrF9OMsglVbK6ukqMJsUCeucGAFTBcMA=="],
|
||||
|
||||
|
|
@ -149,11 +168,11 @@
|
|||
|
||||
"@sapphire/snowflake": ["@sapphire/snowflake@3.5.3", "", {}, "sha512-jjmJywLAFoWeBi1W7994zZyiNWPIiqRRNAmSERxyg93xRGzNYvGjlZ0gR6x0F4gPRi2+0O6S71kOZYyr3cxaIQ=="],
|
||||
|
||||
"@types/bun": ["@types/bun@1.2.16", "", { "dependencies": { "bun-types": "1.2.16" } }, "sha512-1aCZJ/6nSiViw339RsaNhkNoEloLaPzZhxMOYEa7OzRzO41IGg5n/7I43/ZIAW/c+Q6cT12Vf7fOZOoVIzb5BQ=="],
|
||||
"@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="],
|
||||
|
||||
"@types/luxon": ["@types/luxon@3.6.2", "", {}, "sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw=="],
|
||||
|
||||
"@types/node": ["@types/node@24.0.1", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-MX4Zioh39chHlDJbKmEgydJDS3tspMP/lnQC67G3SWsTnb9NeYVWOjkxpOSy4oMfPs4StcWHwBrvUb4ybfnuaw=="],
|
||||
"@types/node": ["@types/node@24.0.3", "", { "dependencies": { "undici-types": "~7.8.0" } }, "sha512-R4I/kzCYAdRLzfiCabn9hxWfbuHS573x+r0dJMkkzThEa7pbrcDWK+9zu3e7aBOouf+rQAciqPFMnxwr0aWgKg=="],
|
||||
|
||||
"@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
|
||||
|
||||
|
|
@ -171,13 +190,17 @@
|
|||
|
||||
"@workshop/spike": ["@workshop/spike@workspace:packages/spike"],
|
||||
|
||||
"@workshop/todo": ["@workshop/todo@workspace:packages/todo"],
|
||||
|
||||
"accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="],
|
||||
|
||||
"ajv": ["ajv@6.12.6", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g=="],
|
||||
|
||||
"attache": ["attache@workspace:packages/attache"],
|
||||
|
||||
"body-parser": ["body-parser@2.2.0", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.0", "http-errors": "^2.0.0", "iconv-lite": "^0.6.3", "on-finished": "^2.4.1", "qs": "^6.14.0", "raw-body": "^3.0.0", "type-is": "^2.0.0" } }, "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg=="],
|
||||
|
||||
"bun-types": ["bun-types@1.2.16", "", { "dependencies": { "@types/node": "*" } }, "sha512-ciXLrHV4PXax9vHvUrkvun9VPVGOVwbbbBF/Ev1cXz12lyEZMoJpIJABOfPcN9gDJRaiKF9MVbSygLg4NXu3/A=="],
|
||||
"bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
|
||||
|
||||
"bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="],
|
||||
|
||||
|
|
@ -203,9 +226,9 @@
|
|||
|
||||
"depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="],
|
||||
|
||||
"discord-api-types": ["discord-api-types@0.38.11", "", {}, "sha512-XN0qhcQpetkyb/49hcDHuoeUPsQqOkb17wbV/t48gUkoEDi4ajhsxqugGcxvcN17BBtI9FPPWEgzv6IhQmCwyw=="],
|
||||
"discord-api-types": ["discord-api-types@0.38.12", "", {}, "sha512-vqkRM50N5Zc6OVckAqtSslbUEoXmpN4bd9xq2jkoK9fgO3KNRIOyMMQ7ipqjwjKuAgzWvU6G8bRIcYWaUe1sCA=="],
|
||||
|
||||
"discord.js": ["discord.js@14.19.3", "", { "dependencies": { "@discordjs/builders": "^1.11.2", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.5.0", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.2", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.1", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.1" } }, "sha512-lncTRk0k+8Q5D3nThnODBR8fR8x2fM798o8Vsr40Krx0DjPwpZCuxxTcFMrXMQVOqM1QB9wqWgaXPg3TbmlHqA=="],
|
||||
"discord.js": ["discord.js@14.20.0", "", { "dependencies": { "@discordjs/builders": "^1.11.2", "@discordjs/collection": "1.5.3", "@discordjs/formatters": "^0.6.1", "@discordjs/rest": "^2.5.1", "@discordjs/util": "^1.1.1", "@discordjs/ws": "^1.2.3", "@sapphire/snowflake": "3.5.3", "discord-api-types": "^0.38.1", "fast-deep-equal": "3.1.3", "lodash.snakecase": "4.1.1", "magic-bytes.js": "^1.10.0", "tslib": "^2.6.3", "undici": "6.21.3" } }, "sha512-5fRTptK2vpuz+bTuAEUQLSo/3AgCSLHl6Mm9+/ofb+8cbbnjWllhtaqRBq7XcpzlBnfNEugKv8HvCwcOtIHpCg=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
|
|
@ -229,7 +252,7 @@
|
|||
|
||||
"express": ["express@5.1.0", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.0", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA=="],
|
||||
|
||||
"express-rate-limit": ["express-rate-limit@7.5.0", "", { "peerDependencies": { "express": "^4.11 || 5 || ^5.0.0-beta.1" } }, "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg=="],
|
||||
"express-rate-limit": ["express-rate-limit@7.5.1", "", { "peerDependencies": { "express": ">= 4.11" } }, "sha512-7iN8iPMDzOMHPUYllBEsQdWVB6fPDMPqwjBaFrgr4Jgr/+okjvzAy+UHlYYL/Vs0OsOrMkwS6PJDkFlJwoxUnw=="],
|
||||
|
||||
"fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="],
|
||||
|
||||
|
|
@ -253,7 +276,7 @@
|
|||
|
||||
"hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="],
|
||||
|
||||
"hono": ["hono@4.7.11", "", {}, "sha512-rv0JMwC0KALbbmwJDEnxvQCeJh+xbS3KEWW5PC9cMJ08Ur9xgatI0HmtgYZfOdOSOeYsp5LO2cOhdI8cLEbDEQ=="],
|
||||
"hono": ["hono@4.8.2", "", {}, "sha512-hM+1RIn9PK1I6SiTNS6/y7O1mvg88awYLFEuEtoiMtRyT3SD2iu9pSFgbBXT3b1Ua4IwzvSTLvwO0SEhDxCi4w=="],
|
||||
|
||||
"http-errors": ["http-errors@2.0.0", "", { "dependencies": { "depd": "2.0.0", "inherits": "2.0.4", "setprototypeof": "1.2.0", "statuses": "2.0.1", "toidentifier": "1.0.1" } }, "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ=="],
|
||||
|
||||
|
|
@ -289,6 +312,8 @@
|
|||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"nanoid": ["nanoid@5.1.5", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw=="],
|
||||
|
||||
"negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
|
|
@ -299,7 +324,7 @@
|
|||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"openai": ["openai@5.3.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-VIKmoF7y4oJCDOwP/oHXGzM69+x0dpGFmN9QmYO+uPbLFOmmnwO+x1GbsgUtI+6oraxomGZ566Y421oYVu191w=="],
|
||||
"openai": ["openai@5.6.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-jNH5z+hYAdOMZXyEt0yZ7246s+UZjg2AwFQqkAhZIPPjxNtHHO5mykOefau6FkOqj16aC94MOdJl/rZBcKj/cQ=="],
|
||||
|
||||
"parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="],
|
||||
|
||||
|
|
@ -349,8 +374,6 @@
|
|||
|
||||
"style-mod": ["style-mod@4.1.2", "", {}, "sha512-wnD1HyVqpJUI2+eKZ+eo1UwghftP6yuFheBqqe+bWCotBjC2K1YnteJILRMs3SM4V/0dLEW1SC27MWP5y+mwmw=="],
|
||||
|
||||
"text-do": ["text-do@workspace:packages/text-do"],
|
||||
|
||||
"toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="],
|
||||
|
||||
"ts-mixer": ["ts-mixer@6.0.4", "", {}, "sha512-ufKpbmrugz5Aou4wcr5Wc1UUFWOLhq+Fm6qa6P0w0K5Qw2yhaUoiWszhCVuNQyNwrlGiscHOmqYoAox1PtvgjA=="],
|
||||
|
|
@ -361,7 +384,7 @@
|
|||
|
||||
"typescript": ["typescript@5.8.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ=="],
|
||||
|
||||
"undici": ["undici@6.21.1", "", {}, "sha512-q/1rj5D0/zayJB2FraXdaWxbhWiNKDvu8naDT2dl1yTlvJp4BLtOcp2a5BvgGNQpYYJzau7tf1WgKv3b+7mqpQ=="],
|
||||
"undici": ["undici@6.21.3", "", {}, "sha512-gBLkYIlEnSp8pFbT64yFgGE6UIB9tAkhukC23PmMDCe5Nd+cRqKxSjw5y54MK2AZMgZfJWMaNE4nYUHgi1XEOw=="],
|
||||
|
||||
"undici-types": ["undici-types@7.8.0", "", {}, "sha512-9UJ2xGDvQ43tYyVMpuHlsgApydB8ZKfVYTsLDhXkFL/6gfkp+U8xTGdh8pMJv1SpZna0zxG1DwsKZsreLbXBxw=="],
|
||||
|
||||
|
|
@ -379,7 +402,7 @@
|
|||
|
||||
"ws": ["ws@8.18.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ=="],
|
||||
|
||||
"zod": ["zod@3.25.64", "", {}, "sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g=="],
|
||||
"zod": ["zod@3.25.67", "", {}, "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw=="],
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.24.5", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g=="],
|
||||
|
||||
|
|
@ -387,16 +410,6 @@
|
|||
|
||||
"@discordjs/ws/@discordjs/collection": ["@discordjs/collection@2.1.1", "", {}, "sha512-LiSusze9Tc7qF03sLCujF5iZp7K+vRNEDBZ86FT9aQAv3vxMLihUvKvpsCWiQ2DJq1tVckopKm1rxomgNUc9hg=="],
|
||||
|
||||
"@workshop/spike/@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="],
|
||||
|
||||
"http-errors/statuses": ["statuses@2.0.1", "", {}, "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ=="],
|
||||
|
||||
"text-do/@types/bun": ["@types/bun@1.2.17", "", { "dependencies": { "bun-types": "1.2.17" } }, "sha512-l/BYs/JYt+cXA/0+wUhulYJB6a6p//GTPiJ7nV+QHa8iiId4HZmnu/3J/SowP5g0rTiERY2kfGKXEK5Ehltx4Q=="],
|
||||
|
||||
"text-do/hono": ["hono@4.8.2", "", {}, "sha512-hM+1RIn9PK1I6SiTNS6/y7O1mvg88awYLFEuEtoiMtRyT3SD2iu9pSFgbBXT3b1Ua4IwzvSTLvwO0SEhDxCi4w=="],
|
||||
|
||||
"@workshop/spike/@types/bun/bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
|
||||
|
||||
"text-do/@types/bun/bun-types": ["bun-types@1.2.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ElC7ItwT3SCQwYZDYoAH+q6KT4Fxjl8DtZ6qDulUFBmXA8YB4xo+l54J9ZJN+k2pphfn9vk7kfubeSd5QfTVJQ=="],
|
||||
}
|
||||
}
|
||||
|
|
|
|||
11
package.json
11
package.json
|
|
@ -1,9 +1,14 @@
|
|||
{
|
||||
"name": "workshop",
|
||||
"private": true,
|
||||
"workspaces": [
|
||||
"packages/*"
|
||||
],
|
||||
"workspaces": {
|
||||
"packages": [
|
||||
"packages/*"
|
||||
],
|
||||
"catalog": {
|
||||
"hono": "^4.8.0"
|
||||
}
|
||||
},
|
||||
"prettier": {
|
||||
"printWidth": 110,
|
||||
"semi": false
|
||||
|
|
|
|||
|
|
@ -12,9 +12,10 @@
|
|||
"semi": false
|
||||
},
|
||||
"dependencies": {
|
||||
"hono": "^4.7.11",
|
||||
"hono": "catalog:",
|
||||
"@workshop/nano-remix": "workspace:*",
|
||||
"@workshop/shared": "workspace:*"
|
||||
"@workshop/shared": "workspace:*",
|
||||
"@workshop/todo": "workspace:*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
|
|
|
|||
|
|
@ -1,115 +0,0 @@
|
|||
import KV from "@workshop/shared/kv"
|
||||
import { Form, type Head, type LoaderProps, useAction } from "@workshop/nano-remix"
|
||||
import { addReminder, deleteReminder, type Reminder } from "@workshop/shared/reminders"
|
||||
import { CreateReminder } from "@/components/createReminder"
|
||||
|
||||
export const head: Head = {
|
||||
title: "Todos",
|
||||
scripts: [{ src: "https://cdn.tailwindcss.com" }],
|
||||
}
|
||||
|
||||
export const loader = async (req: Request) => {
|
||||
const todos = await KV.get("todos", {})
|
||||
return { todos }
|
||||
}
|
||||
|
||||
export const action = async (req: Request) => {
|
||||
const formData = await req.formData()
|
||||
const action = formData.get("_action") as string
|
||||
|
||||
// Handle delete action
|
||||
if (action === "delete") {
|
||||
const id = formData.get("id") as string
|
||||
if (!id) {
|
||||
return { error: "Reminder ID is required" }
|
||||
}
|
||||
|
||||
try {
|
||||
await deleteReminder(id)
|
||||
return { success: true, message: "Reminder deleted successfully" }
|
||||
} catch (error) {
|
||||
return { error: `Failed to delete reminder: ${error}` }
|
||||
}
|
||||
} else if (action === "create") {
|
||||
// Handle create action (default)
|
||||
const title = formData.get("title") as string
|
||||
const dueDate = formData.get("dueDate") as string
|
||||
const assignee = (formData.get("assignee") as string) || undefined
|
||||
|
||||
if (!title) {
|
||||
return { error: "Title is required" }
|
||||
}
|
||||
|
||||
if (!dueDate) {
|
||||
return { error: "Due date is required" }
|
||||
}
|
||||
|
||||
try {
|
||||
const newReminder = await addReminder(title, dueDate, assignee as any)
|
||||
return { success: true, newReminder }
|
||||
} catch (error) {
|
||||
return { error: `Failed to create reminder: ${error}` }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default (props: LoaderProps<typeof loader>) => {
|
||||
const { data, loading, error } = useAction()
|
||||
|
||||
return (
|
||||
<div class="bg-blue-50 min-h-screen pb-8">
|
||||
<h1 class="p-4 text-2xl bg-blue-100 text-blue-800">Reminders</h1>
|
||||
<main class="p-4">
|
||||
<div>
|
||||
<h2 class="text-xl font-medium text-blue-800 mb-4">Current Reminders</h2>
|
||||
<Reminders reminders={props.reminders} />
|
||||
</div>
|
||||
<div class="mt-8">
|
||||
<CreateReminder loading={loading} success={data?.success} error={error ?? data?.error} />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
const Reminders = ({ reminders }: { reminders: Reminder[] }) => {
|
||||
return (
|
||||
<>
|
||||
{reminders.length === 0 ? (
|
||||
<p class="text-gray-600">No reminders found.</p>
|
||||
) : (
|
||||
<ul class="bg-white rounded-lg shadow overflow-hidden divide-y divide-gray-200">
|
||||
{reminders.map((reminder) => (
|
||||
<li key={reminder.id} class="p-4 hover:bg-gray-50">
|
||||
<div class="flex justify-between items-start">
|
||||
<div>
|
||||
<div class="font-medium">{reminder.title}</div>
|
||||
<div class="text-sm text-gray-500 flex justify-between mt-1 items-center gap-2">
|
||||
<span>Due: {new Date(reminder.dueDate).toLocaleString()}</span>
|
||||
{reminder.assignee && (
|
||||
<span class="bg-blue-100 text-blue-800 text-xs px-2 py-1 rounded ml-2">
|
||||
{reminder.assignee}
|
||||
</span>
|
||||
)}
|
||||
<div class="text-sm text-gray-500 flex p-2">{reminder.status}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Form method="POST" class="inline-block">
|
||||
<input type="hidden" name="_action" value="delete" />
|
||||
<input type="hidden" name="id" value={reminder.id} />
|
||||
<button
|
||||
type="submit"
|
||||
class="text-red-600 hover:text-red-800 text-sm font-medium"
|
||||
title="Delete Reminder"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
</Form>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
47
packages/http/src/routes/todos/[id].tsx
Normal file
47
packages/http/src/routes/todos/[id].tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import KV from "@workshop/shared/kv"
|
||||
import { type Head, type LoaderProps } from "@workshop/nano-remix"
|
||||
import { TodoEditor } from "@workshop/todo"
|
||||
|
||||
export const head: Head = {
|
||||
title: "Todos",
|
||||
scripts: [{ src: "https://cdn.tailwindcss.com" }],
|
||||
}
|
||||
|
||||
export const loader = async (req: Request, params: { id: string }) => {
|
||||
const todos = await KV.get("todos", {})
|
||||
return { todos: todos[params.id], todoName: params.id }
|
||||
}
|
||||
|
||||
export const action = async (req: Request, params: { id: string }) => {
|
||||
console.log(`🌭 ACTION ${params}`)
|
||||
const formData = await req.formData()
|
||||
const todosString = formData.get("todos") as string
|
||||
await KV.update("todos", {}, (todos) => {
|
||||
todos[params.id] = todosString
|
||||
return todos
|
||||
})
|
||||
|
||||
return { success: true }
|
||||
}
|
||||
|
||||
export default ({ todos, todoName }: LoaderProps<typeof loader>) => {
|
||||
return (
|
||||
<div class="bg-blue-50 min-h-screen pb-8">
|
||||
<h1 class="p-4 text-2xl bg-blue-100 text-blue-800">Hey</h1>
|
||||
<main class="p-4"></main>
|
||||
<TodoEditor
|
||||
todos={todos}
|
||||
onChange={async (newTodos) => {
|
||||
const formData = new FormData()
|
||||
console.log(`🌭`, todoName)
|
||||
formData.append("todos", newTodos)
|
||||
const response = await fetch(`/todos/${todoName}`, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
headers: { Accept: "application/json" },
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
@ -12,10 +12,7 @@ function startServer(opts: StartOptions) {
|
|||
"/*": (req) => nanoRemix(req, opts),
|
||||
},
|
||||
|
||||
development: process.env.NODE_ENV !== "production" && {
|
||||
hmr: true,
|
||||
console: true,
|
||||
},
|
||||
development: process.env.NODE_ENV !== "production" && { hmr: true, console: true },
|
||||
})
|
||||
|
||||
console.log(`🤖 Server running at ${server.url}`)
|
||||
|
|
@ -25,4 +22,4 @@ if (import.meta.main) {
|
|||
startServer({ routesDir: join(import.meta.dir, "routes") })
|
||||
}
|
||||
|
||||
export { startServer }
|
||||
export { startServer }
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
"typescript": "^5"
|
||||
},
|
||||
"dependencies": {
|
||||
"hono": "^4.7.11"
|
||||
"hono": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest"
|
||||
|
|
|
|||
|
|
@ -4,9 +4,16 @@ import { Form, useAction, wrapComponentWithLoader } from "@/clientHelpers"
|
|||
export { Form, useAction, wrapComponentWithLoader }
|
||||
export { nanoRemix }
|
||||
|
||||
export type Loader<Data extends object> = (req: Request) => Promise<Data> | Data
|
||||
export type Loader<Data extends object> = (
|
||||
req: Request,
|
||||
params: Record<string, string>
|
||||
) => Promise<Data> | Data
|
||||
export type LoaderProps<T> = T extends Loader<infer R> ? Awaited<R> : never
|
||||
export type Action<Data = unknown> = (req: Request) => Promise<Response | Data>
|
||||
|
||||
export type Action<Data = unknown> = (
|
||||
req: Request,
|
||||
params: Record<string, string>
|
||||
) => Promise<Response | Data>
|
||||
|
||||
export type Head = {
|
||||
title?: string
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ export const nanoRemix = async (req: Request, options: Options = {}) => {
|
|||
const basename = ext ? url.pathname.slice(0, -ext.length) : url.pathname
|
||||
const route = router.match(basename)
|
||||
|
||||
console.log(`🌭 requesting ${url} got ${route?.pathname}`)
|
||||
|
||||
if (!route) {
|
||||
return new Response("Route Not Found", {
|
||||
status: 404,
|
||||
|
|
@ -29,7 +31,6 @@ export const nanoRemix = async (req: Request, options: Options = {}) => {
|
|||
}
|
||||
|
||||
const routeName = route.name === "/" ? "/index" : route.name
|
||||
|
||||
if (!ext) {
|
||||
await buildDynamicRoute(distDir, routeName, route.filePath) // Eventually this should be running only on initial build and when a route changes
|
||||
return await renderServer(req, route)
|
||||
|
|
@ -41,7 +42,6 @@ export const nanoRemix = async (req: Request, options: Options = {}) => {
|
|||
headers: { "Content-Type": "text/plain" },
|
||||
})
|
||||
}
|
||||
|
||||
return new Response(file)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,9 +22,9 @@ const handleAction = async (req: Request, route: Bun.MatchedRoute) => {
|
|||
)
|
||||
}
|
||||
|
||||
const actionData = await action(req)
|
||||
const actionData = await action(req, route.params)
|
||||
if (actionData instanceof Response) return actionData // This should only happen if the action wants to redirect
|
||||
const loaderData = await loader?.(req)
|
||||
const loaderData = await loader?.(req, route.params)
|
||||
const result = { actionData, loaderData }
|
||||
|
||||
return new Response(JSON.stringify(result), {
|
||||
|
|
@ -35,8 +35,8 @@ const handleAction = async (req: Request, route: Bun.MatchedRoute) => {
|
|||
|
||||
const renderHtml = async (req: Request, route: Bun.MatchedRoute) => {
|
||||
const component = await import(route.filePath)
|
||||
const loader = component.loader
|
||||
const loaderData = loader ? await loader(req) : {}
|
||||
const loader = component.loader as Loader<any>
|
||||
const loaderData = loader ? await loader(req, route.params) : {}
|
||||
|
||||
const routeName = route.name === "/" ? "/index" : route.name
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs"
|
||||
import { dirname, join } from "node:path"
|
||||
import type { Reminder } from "@/reminders"
|
||||
import type { Todo } from "@workshop/todo"
|
||||
|
||||
export type Conversation = { message: string; role: "user" | "assistant" }
|
||||
export interface Keys {
|
||||
|
|
|
|||
0
packages/spike2/src/cli/index.ts
Normal file
0
packages/spike2/src/cli/index.ts
Normal file
|
|
@ -1 +0,0 @@
|
|||
console.log("Hello via Bun!");
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
{
|
||||
"name": "todo",
|
||||
"module": "index.ts",
|
||||
"name": "@workshop/todo",
|
||||
"module": "src/main.ts",
|
||||
"type": "module",
|
||||
"types": "src/main.ts",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "^6.8.1",
|
||||
|
|
@ -11,8 +12,8 @@
|
|||
"@lezer/generator": "^1.7.3",
|
||||
"@lezer/highlight": "^1.2.1",
|
||||
"@lezer/lr": "^1.4.2",
|
||||
"hono": "^4.8.0",
|
||||
"luxon": "^3.6.1",
|
||||
"hono": "catalog:",
|
||||
"luxon": "^3.6.1"
|
||||
},
|
||||
"prettier": {
|
||||
"semi": false,
|
||||
|
|
@ -22,6 +23,7 @@
|
|||
"@types/bun": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": "^5"
|
||||
"typescript": "^5",
|
||||
"hono": "^4"
|
||||
}
|
||||
}
|
||||
32
packages/todo/src/index.css
Normal file
32
packages/todo/src/index.css
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/* Todo styling */
|
||||
.todo-completed {
|
||||
text-decoration: line-through;
|
||||
color: #6B7280;
|
||||
}
|
||||
|
||||
.todo-tag {
|
||||
color: #3B82F6;
|
||||
}
|
||||
|
||||
.todo-completed .todo-tag, .todo-completed .todo-date {
|
||||
color: #6B7280;
|
||||
}
|
||||
|
||||
.todo-date {
|
||||
color: #10B981;
|
||||
}
|
||||
|
||||
.todo-invalid {
|
||||
text-decoration-line: underline;
|
||||
text-decoration-color: #EF4444;
|
||||
text-decoration-style: wavy;
|
||||
}
|
||||
|
||||
.todo-header {
|
||||
font-size: 1.25rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.todo-filtered {
|
||||
background-color: greenyellow;
|
||||
}
|
||||
|
|
@ -1,3 +1,3 @@
|
|||
export {TodoEditor } from "@/todoEditor"
|
||||
export {parseTodoLine, parseTodoList} from "@/todo"
|
||||
export type {Todo} from "@/todo"
|
||||
export { TodoEditor } from "@/todoEditor"
|
||||
export { parseTodoLine, parseTodoList } from "@/todo"
|
||||
export type { Todo } from "@/todo"
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { parseTodoLine, Todo } from "@/todo/todo"
|
||||
import { parseTodoLine, type Todo } from "@/todo"
|
||||
import { RangeSetBuilder, StateEffect } from "@codemirror/state"
|
||||
import { EditorView, Decoration, ViewPlugin, ViewUpdate } from "@codemirror/view"
|
||||
import { RefObject } from "hono/jsx"
|
||||
import { type RefObject } from "hono/jsx"
|
||||
|
||||
// Effect to trigger a decoration refresh on filter change
|
||||
export const refreshFilterEffect = StateEffect.define<void>()
|
||||
|
|
|
|||
|
|
@ -1,17 +1,22 @@
|
|||
import { useCallback, useEffect, useRef, useState } from "hono/jsx"
|
||||
import { useCallback, useEffect, useRef, useState, type KeyboardEvent } from "hono/jsx"
|
||||
import { defaultKeymap, history, historyKeymap } from "@codemirror/commands"
|
||||
import { EditorState } from "@codemirror/state"
|
||||
import { EditorView, lineNumbers, keymap } from "@codemirror/view"
|
||||
import { keymap as viewKeymap } from "@codemirror/view" // ensure keymap is imported from view if not already
|
||||
import { foldGutter, foldKeymap } from "@codemirror/language"
|
||||
import { refreshFilterEffect, todoDecorations } from "@/todo/todoDecorations"
|
||||
import { autoTodoOnNewline } from "@/todo/autoTodoOnNewline"
|
||||
import { todoKeymap } from "@/todo/todoKeymap"
|
||||
import { refreshFilterEffect, todoDecorations } from "@/todoDecorations"
|
||||
import { autoTodoOnNewline } from "@/autoTodoOnNewline"
|
||||
import { todoKeymap } from "@/todoKeymap"
|
||||
import { DateTime } from "luxon"
|
||||
|
||||
import "./index.css"
|
||||
|
||||
export const TodoEditor = () => {
|
||||
type TodoEditorProps = {
|
||||
todos?: string
|
||||
onChange: (todos: string) => void
|
||||
}
|
||||
|
||||
export const TodoEditor = ({ todos, onChange }: TodoEditorProps) => {
|
||||
const editorContainer = useRef<HTMLDivElement>(null)
|
||||
const editorRef = useRef<EditorView>(null)
|
||||
const [filter, setFilter] = useState<string>("")
|
||||
|
|
@ -28,13 +33,14 @@ export const TodoEditor = () => {
|
|||
if (!editorContainer.current) return
|
||||
|
||||
const changeListener = EditorView.updateListener.of((update) => {
|
||||
localStorage.setItem("todo-editor-content", update.state.doc.toString())
|
||||
if (update.docChanged) {
|
||||
onChange(update.state.doc.toString())
|
||||
}
|
||||
})
|
||||
|
||||
const savedDoc = localStorage.getItem("todo-editor-content")
|
||||
const docText = savedDoc ?? defaultDoc
|
||||
// Use todos prop for content
|
||||
const state = EditorState.create({
|
||||
doc: docText,
|
||||
doc: todos || defaultDoc,
|
||||
extensions: [
|
||||
foldGutter(),
|
||||
lineNumbers(),
|
||||
|
|
@ -54,7 +60,7 @@ export const TodoEditor = () => {
|
|||
editorRef.current = view
|
||||
|
||||
return () => view.destroy()
|
||||
}, [])
|
||||
}, [todos, onChange])
|
||||
|
||||
const filterInput = useCallback((e: KeyboardEvent) => {
|
||||
if (e.key === "Enter" || e.key === "Escape") {
|
||||
|
|
@ -67,7 +73,7 @@ export const TodoEditor = () => {
|
|||
}, [])
|
||||
|
||||
return (
|
||||
<div class="h-dvh w-dvw flex flex-col">
|
||||
<div class="h-full w-full flex flex-col ">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Filter by tag"
|
||||
|
|
@ -82,18 +88,16 @@ export const TodoEditor = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export default App
|
||||
|
||||
const defaultDoc = `
|
||||
# Today (Group tasks by when they are due)
|
||||
|
||||
- [ ] Sample task with a due date @${DateTime.local().toFormat("yyyy/MM/dd")}
|
||||
- [ ] Sample task with a due date @${DateTime.local().toFormat("MM/dd/yyyy")}
|
||||
- [ ] You can use a #tag to filter tasks
|
||||
- [ ] A sub task! Create nested tasks by indenting with <tab>
|
||||
- [x] Complete a task by pressing <opt+k>
|
||||
|
||||
# This week
|
||||
- [ ] Another task with a due date @${DateTime.local().plus({ days: 3 }).toFormat("yyyy/MM/dd")}
|
||||
- [ ] Another task with a due date @${DateTime.local().plus({ days: 3 }).toFormat("MM/dd/yyyy")}
|
||||
|
||||
# Later
|
||||
- [ ] I use later as a junk drawer for tasks I don't want to forget
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { indentMore, indentLess } from "@codemirror/commands"
|
||||
import { EditorView, keymap } from "@codemirror/view"
|
||||
import { parseTodoLine, todoToText } from "./todo"
|
||||
import { RefObject } from "hono/jsx"
|
||||
import { type RefObject } from "hono/jsx"
|
||||
|
||||
export const todoKeymap = (filterElRef: RefObject<HTMLInputElement>) => {
|
||||
return keymap.of([
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
// Environment setup & latest features
|
||||
"lib": ["ESNext"],
|
||||
"lib": ["ESNext", "DOM"],
|
||||
"target": "ESNext",
|
||||
"module": "Preserve",
|
||||
"moduleDetection": "force",
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "hono/jsx",
|
||||
"allowJs": true,
|
||||
|
||||
// Bundler mode
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user