├── .gitignore ├── LICENSE ├── README.md ├── assets └── solid.png ├── deno.json ├── deno.lock ├── import_map.json ├── main.ts ├── src ├── app-menus.tsx ├── app.tsx ├── components │ └── text.tsx ├── contentview.tsx ├── examples │ ├── counter.tsx │ ├── index.tsx │ └── todo.tsx ├── hooks │ └── use-color-scheme.ts ├── pages │ └── common.ts ├── sidebar.tsx └── state.tsx └── tools ├── renderer.js └── scripts └── bundle-solid.ts /.gitignore: -------------------------------------------------------------------------------- 1 | dist -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright OpenJS Foundation and other contributors, https://openjsf.org 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Build Solid macOS apps 2 | 3 | Use this starter template to create your own macOS app built with [Solid](https://solidjs.com). 4 | 5 | ```bash 6 | git clone https://github.com/NativeScript/template-macos-solid 7 | cd template-macos-solid 8 | 9 | deno task bundle 10 | deno task start 11 | ``` 12 | 13 | 14 | 15 | https://github.com/user-attachments/assets/183ae2d4-7254-4f83-97da-d92bd134a796 16 | 17 | https://github.com/user-attachments/assets/ff28e2d4-00f5-4202-bd0d-eba420c2bcb1 18 | 19 | 20 | -------------------------------------------------------------------------------- /assets/solid.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NativeScript/template-macos-solid/2c9d878745f9c1ec9ac7360f50d68e93cb2f0c68/assets/solid.png -------------------------------------------------------------------------------- /deno.json: -------------------------------------------------------------------------------- 1 | { 2 | "tasks": { 3 | "start": "deno task bundle && deno task run-native", 4 | "bundle": "deno run -A ./tools/scripts/bundle-solid.ts \"src/app.tsx\" \"dist/out.js\"", 5 | "run-native": "deno run -A --unstable-sloppy-imports ./main.ts" 6 | }, 7 | "importMap": "./import_map.json", 8 | "lint": { 9 | "include": [ 10 | "jsx", 11 | "native", 12 | "scripts", 13 | "src" 14 | ], 15 | "rules": { 16 | "exclude": [ 17 | "no-explicit-any", 18 | "no-var", 19 | "no-empty-interface", 20 | "ban-types" 21 | ] 22 | } 23 | }, 24 | "fmt": { 25 | "include": [ 26 | "jsx", 27 | "native", 28 | "scripts", 29 | "src", 30 | "deno.json", 31 | "import_map.json", 32 | "README.md" 33 | ] 34 | }, 35 | "compilerOptions": { 36 | "jsxImportSource": "@jsx", 37 | "jsx": "react-jsx", 38 | "experimentalDecorators": true, 39 | "emitDecoratorMetadata": true, 40 | "noImplicitOverride": false 41 | } 42 | } -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "4", 3 | "specifiers": { 4 | "jsr:@luca/esbuild-deno-loader@0.11": "0.11.0", 5 | "jsr:@std/bytes@^1.0.2": "1.0.2", 6 | "jsr:@std/encoding@^1.0.5": "1.0.5", 7 | "jsr:@std/path@^1.0.6": "1.0.7", 8 | "npm:@nativescript/foundation@*": "0.0.1-alpha.0", 9 | "npm:@nativescript/foundation@0.0.1-alpha.19": "0.0.1-alpha.19", 10 | "npm:@nativescript/foundation@0.0.1-alpha.22": "0.0.1-alpha.22", 11 | "npm:@nativescript/macos-node-api@~0.1.3": "0.1.3", 12 | "npm:esbuild-plugin-solid@*": "0.6.0_esbuild@0.24.0_solid-js@1.9.3__seroval@1.1.1_@babel+core@7.26.0", 13 | "npm:esbuild@*": "0.24.0", 14 | "npm:solid-js@*": "1.9.3_seroval@1.1.1", 15 | "npm:yoga-layout@3.1.0": "3.1.0" 16 | }, 17 | "jsr": { 18 | "@luca/esbuild-deno-loader@0.11.0": { 19 | "integrity": "c05a989aa7c4ee6992a27be5f15cfc5be12834cab7ff84cabb47313737c51a2c", 20 | "dependencies": [ 21 | "jsr:@std/bytes", 22 | "jsr:@std/encoding", 23 | "jsr:@std/path" 24 | ] 25 | }, 26 | "@std/bytes@1.0.2": { 27 | "integrity": "fbdee322bbd8c599a6af186a1603b3355e59a5fb1baa139f8f4c3c9a1b3e3d57" 28 | }, 29 | "@std/encoding@1.0.5": { 30 | "integrity": "ecf363d4fc25bd85bd915ff6733a7e79b67e0e7806334af15f4645c569fefc04" 31 | }, 32 | "@std/path@1.0.7": { 33 | "integrity": "76a689e07f0e15dcc6002ec39d0866797e7156629212b28f27179b8a5c3b33a1" 34 | } 35 | }, 36 | "npm": { 37 | "@ampproject/remapping@2.3.0": { 38 | "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", 39 | "dependencies": [ 40 | "@jridgewell/gen-mapping", 41 | "@jridgewell/trace-mapping" 42 | ] 43 | }, 44 | "@babel/code-frame@7.26.2": { 45 | "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", 46 | "dependencies": [ 47 | "@babel/helper-validator-identifier", 48 | "js-tokens", 49 | "picocolors" 50 | ] 51 | }, 52 | "@babel/compat-data@7.26.2": { 53 | "integrity": "sha512-Z0WgzSEa+aUcdiJuCIqgujCshpMWgUpgOxXotrYPSA53hA3qopNaqcJpyr0hVb1FeWdnqFA35/fUtXgBK8srQg==" 54 | }, 55 | "@babel/core@7.26.0": { 56 | "integrity": "sha512-i1SLeK+DzNnQ3LL/CswPCa/E5u4lh1k6IAEphON8F+cXt0t9euTshDru0q7/IqMa1PMPz5RnHuHscF8/ZJsStg==", 57 | "dependencies": [ 58 | "@ampproject/remapping", 59 | "@babel/code-frame", 60 | "@babel/generator", 61 | "@babel/helper-compilation-targets", 62 | "@babel/helper-module-transforms", 63 | "@babel/helpers", 64 | "@babel/parser", 65 | "@babel/template", 66 | "@babel/traverse", 67 | "@babel/types", 68 | "convert-source-map", 69 | "debug", 70 | "gensync", 71 | "json5", 72 | "semver" 73 | ] 74 | }, 75 | "@babel/generator@7.26.2": { 76 | "integrity": "sha512-zevQbhbau95nkoxSq3f/DC/SC+EEOUZd3DYqfSkMhY2/wfSeaHV1Ew4vk8e+x8lja31IbyuUa2uQ3JONqKbysw==", 77 | "dependencies": [ 78 | "@babel/parser", 79 | "@babel/types", 80 | "@jridgewell/gen-mapping", 81 | "@jridgewell/trace-mapping", 82 | "jsesc" 83 | ] 84 | }, 85 | "@babel/helper-annotate-as-pure@7.25.9": { 86 | "integrity": "sha512-gv7320KBUFJz1RnylIg5WWYPRXKZ884AGkYpgpWW02TH66Dl+HaC1t1CKd0z3R4b6hdYEcmrNZHUmfCP+1u3/g==", 87 | "dependencies": [ 88 | "@babel/types" 89 | ] 90 | }, 91 | "@babel/helper-compilation-targets@7.25.9": { 92 | "integrity": "sha512-j9Db8Suy6yV/VHa4qzrj9yZfZxhLWQdVnRlXxmKLYlhWUVB1sB2G5sxuWYXk/whHD9iW76PmNzxZ4UCnTQTVEQ==", 93 | "dependencies": [ 94 | "@babel/compat-data", 95 | "@babel/helper-validator-option", 96 | "browserslist", 97 | "lru-cache", 98 | "semver" 99 | ] 100 | }, 101 | "@babel/helper-create-class-features-plugin@7.25.9_@babel+core@7.26.0": { 102 | "integrity": "sha512-UTZQMvt0d/rSz6KI+qdu7GQze5TIajwTS++GUozlw8VBJDEOAqSXwm1WvmYEZwqdqSGQshRocPDqrt4HBZB3fQ==", 103 | "dependencies": [ 104 | "@babel/core", 105 | "@babel/helper-annotate-as-pure", 106 | "@babel/helper-member-expression-to-functions", 107 | "@babel/helper-optimise-call-expression", 108 | "@babel/helper-replace-supers", 109 | "@babel/helper-skip-transparent-expression-wrappers", 110 | "@babel/traverse", 111 | "semver" 112 | ] 113 | }, 114 | "@babel/helper-member-expression-to-functions@7.25.9": { 115 | "integrity": "sha512-wbfdZ9w5vk0C0oyHqAJbc62+vet5prjj01jjJ8sKn3j9h3MQQlflEdXYvuqRWjHnM12coDEqiC1IRCi0U/EKwQ==", 116 | "dependencies": [ 117 | "@babel/traverse", 118 | "@babel/types" 119 | ] 120 | }, 121 | "@babel/helper-module-imports@7.18.6": { 122 | "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", 123 | "dependencies": [ 124 | "@babel/types" 125 | ] 126 | }, 127 | "@babel/helper-module-imports@7.25.9": { 128 | "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", 129 | "dependencies": [ 130 | "@babel/traverse", 131 | "@babel/types" 132 | ] 133 | }, 134 | "@babel/helper-module-transforms@7.26.0_@babel+core@7.26.0": { 135 | "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", 136 | "dependencies": [ 137 | "@babel/core", 138 | "@babel/helper-module-imports@7.25.9", 139 | "@babel/helper-validator-identifier", 140 | "@babel/traverse" 141 | ] 142 | }, 143 | "@babel/helper-optimise-call-expression@7.25.9": { 144 | "integrity": "sha512-FIpuNaz5ow8VyrYcnXQTDRGvV6tTjkNtCK/RYNDXGSLlUD6cBuQTSw43CShGxjvfBTfcUA/r6UhUCbtYqkhcuQ==", 145 | "dependencies": [ 146 | "@babel/types" 147 | ] 148 | }, 149 | "@babel/helper-plugin-utils@7.25.9": { 150 | "integrity": "sha512-kSMlyUVdWe25rEsRGviIgOWnoT/nfABVWlqt9N19/dIPWViAOW2s9wznP5tURbs/IDuNk4gPy3YdYRgH3uxhBw==" 151 | }, 152 | "@babel/helper-replace-supers@7.25.9_@babel+core@7.26.0": { 153 | "integrity": "sha512-IiDqTOTBQy0sWyeXyGSC5TBJpGFXBkRynjBeXsvbhQFKj2viwJC76Epz35YLU1fpe/Am6Vppb7W7zM4fPQzLsQ==", 154 | "dependencies": [ 155 | "@babel/core", 156 | "@babel/helper-member-expression-to-functions", 157 | "@babel/helper-optimise-call-expression", 158 | "@babel/traverse" 159 | ] 160 | }, 161 | "@babel/helper-simple-access@7.25.9": { 162 | "integrity": "sha512-c6WHXuiaRsJTyHYLJV75t9IqsmTbItYfdj99PnzYGQZkYKvan5/2jKJ7gu31J3/BJ/A18grImSPModuyG/Eo0Q==", 163 | "dependencies": [ 164 | "@babel/traverse", 165 | "@babel/types" 166 | ] 167 | }, 168 | "@babel/helper-skip-transparent-expression-wrappers@7.25.9": { 169 | "integrity": "sha512-K4Du3BFa3gvyhzgPcntrkDgZzQaq6uozzcpGbOO1OEJaI+EJdqWIMTLgFgQf6lrfiDFo5FU+BxKepI9RmZqahA==", 170 | "dependencies": [ 171 | "@babel/traverse", 172 | "@babel/types" 173 | ] 174 | }, 175 | "@babel/helper-string-parser@7.25.9": { 176 | "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==" 177 | }, 178 | "@babel/helper-validator-identifier@7.25.9": { 179 | "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==" 180 | }, 181 | "@babel/helper-validator-option@7.25.9": { 182 | "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==" 183 | }, 184 | "@babel/helpers@7.26.0": { 185 | "integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==", 186 | "dependencies": [ 187 | "@babel/template", 188 | "@babel/types" 189 | ] 190 | }, 191 | "@babel/parser@7.26.2": { 192 | "integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==", 193 | "dependencies": [ 194 | "@babel/types" 195 | ] 196 | }, 197 | "@babel/plugin-syntax-jsx@7.25.9_@babel+core@7.26.0": { 198 | "integrity": "sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==", 199 | "dependencies": [ 200 | "@babel/core", 201 | "@babel/helper-plugin-utils" 202 | ] 203 | }, 204 | "@babel/plugin-syntax-typescript@7.25.9_@babel+core@7.26.0": { 205 | "integrity": "sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==", 206 | "dependencies": [ 207 | "@babel/core", 208 | "@babel/helper-plugin-utils" 209 | ] 210 | }, 211 | "@babel/plugin-transform-modules-commonjs@7.25.9_@babel+core@7.26.0": { 212 | "integrity": "sha512-dwh2Ol1jWwL2MgkCzUSOvfmKElqQcuswAZypBSUsScMXvgdT8Ekq5YA6TtqpTVWH+4903NmboMuH1o9i8Rxlyg==", 213 | "dependencies": [ 214 | "@babel/core", 215 | "@babel/helper-module-transforms", 216 | "@babel/helper-plugin-utils", 217 | "@babel/helper-simple-access" 218 | ] 219 | }, 220 | "@babel/plugin-transform-typescript@7.25.9_@babel+core@7.26.0": { 221 | "integrity": "sha512-7PbZQZP50tzv2KGGnhh82GSyMB01yKY9scIjf1a+GfZCtInOWqUH5+1EBU4t9fyR5Oykkkc9vFTs4OHrhHXljQ==", 222 | "dependencies": [ 223 | "@babel/core", 224 | "@babel/helper-annotate-as-pure", 225 | "@babel/helper-create-class-features-plugin", 226 | "@babel/helper-plugin-utils", 227 | "@babel/helper-skip-transparent-expression-wrappers", 228 | "@babel/plugin-syntax-typescript" 229 | ] 230 | }, 231 | "@babel/preset-typescript@7.26.0_@babel+core@7.26.0": { 232 | "integrity": "sha512-NMk1IGZ5I/oHhoXEElcm+xUnL/szL6xflkFZmoEU9xj1qSJXpiS7rsspYo92B4DRCDvZn2erT5LdsCeXAKNCkg==", 233 | "dependencies": [ 234 | "@babel/core", 235 | "@babel/helper-plugin-utils", 236 | "@babel/helper-validator-option", 237 | "@babel/plugin-syntax-jsx", 238 | "@babel/plugin-transform-modules-commonjs", 239 | "@babel/plugin-transform-typescript" 240 | ] 241 | }, 242 | "@babel/template@7.25.9": { 243 | "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", 244 | "dependencies": [ 245 | "@babel/code-frame", 246 | "@babel/parser", 247 | "@babel/types" 248 | ] 249 | }, 250 | "@babel/traverse@7.25.9": { 251 | "integrity": "sha512-ZCuvfwOwlz/bawvAuvcj8rrithP2/N55Tzz342AkTvq4qaWbGfmCk/tKhNaV2cthijKrPAA8SRJV5WWe7IBMJw==", 252 | "dependencies": [ 253 | "@babel/code-frame", 254 | "@babel/generator", 255 | "@babel/parser", 256 | "@babel/template", 257 | "@babel/types", 258 | "debug", 259 | "globals" 260 | ] 261 | }, 262 | "@babel/types@7.26.0": { 263 | "integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==", 264 | "dependencies": [ 265 | "@babel/helper-string-parser", 266 | "@babel/helper-validator-identifier" 267 | ] 268 | }, 269 | "@esbuild/aix-ppc64@0.24.0": { 270 | "integrity": "sha512-WtKdFM7ls47zkKHFVzMz8opM7LkcsIp9amDUBIAWirg70RM71WRSjdILPsY5Uv1D42ZpUfaPILDlfactHgsRkw==" 271 | }, 272 | "@esbuild/android-arm64@0.24.0": { 273 | "integrity": "sha512-Vsm497xFM7tTIPYK9bNTYJyF/lsP590Qc1WxJdlB6ljCbdZKU9SY8i7+Iin4kyhV/KV5J2rOKsBQbB77Ab7L/w==" 274 | }, 275 | "@esbuild/android-arm@0.24.0": { 276 | "integrity": "sha512-arAtTPo76fJ/ICkXWetLCc9EwEHKaeya4vMrReVlEIUCAUncH7M4bhMQ+M9Vf+FFOZJdTNMXNBrWwW+OXWpSew==" 277 | }, 278 | "@esbuild/android-x64@0.24.0": { 279 | "integrity": "sha512-t8GrvnFkiIY7pa7mMgJd7p8p8qqYIz1NYiAoKc75Zyv73L3DZW++oYMSHPRarcotTKuSs6m3hTOa5CKHaS02TQ==" 280 | }, 281 | "@esbuild/darwin-arm64@0.24.0": { 282 | "integrity": "sha512-CKyDpRbK1hXwv79soeTJNHb5EiG6ct3efd/FTPdzOWdbZZfGhpbcqIpiD0+vwmpu0wTIL97ZRPZu8vUt46nBSw==" 283 | }, 284 | "@esbuild/darwin-x64@0.24.0": { 285 | "integrity": "sha512-rgtz6flkVkh58od4PwTRqxbKH9cOjaXCMZgWD905JOzjFKW+7EiUObfd/Kav+A6Gyud6WZk9w+xu6QLytdi2OA==" 286 | }, 287 | "@esbuild/freebsd-arm64@0.24.0": { 288 | "integrity": "sha512-6Mtdq5nHggwfDNLAHkPlyLBpE5L6hwsuXZX8XNmHno9JuL2+bg2BX5tRkwjyfn6sKbxZTq68suOjgWqCicvPXA==" 289 | }, 290 | "@esbuild/freebsd-x64@0.24.0": { 291 | "integrity": "sha512-D3H+xh3/zphoX8ck4S2RxKR6gHlHDXXzOf6f/9dbFt/NRBDIE33+cVa49Kil4WUjxMGW0ZIYBYtaGCa2+OsQwQ==" 292 | }, 293 | "@esbuild/linux-arm64@0.24.0": { 294 | "integrity": "sha512-TDijPXTOeE3eaMkRYpcy3LarIg13dS9wWHRdwYRnzlwlA370rNdZqbcp0WTyyV/k2zSxfko52+C7jU5F9Tfj1g==" 295 | }, 296 | "@esbuild/linux-arm@0.24.0": { 297 | "integrity": "sha512-gJKIi2IjRo5G6Glxb8d3DzYXlxdEj2NlkixPsqePSZMhLudqPhtZ4BUrpIuTjJYXxvF9njql+vRjB2oaC9XpBw==" 298 | }, 299 | "@esbuild/linux-ia32@0.24.0": { 300 | "integrity": "sha512-K40ip1LAcA0byL05TbCQ4yJ4swvnbzHscRmUilrmP9Am7//0UjPreh4lpYzvThT2Quw66MhjG//20mrufm40mA==" 301 | }, 302 | "@esbuild/linux-loong64@0.24.0": { 303 | "integrity": "sha512-0mswrYP/9ai+CU0BzBfPMZ8RVm3RGAN/lmOMgW4aFUSOQBjA31UP8Mr6DDhWSuMwj7jaWOT0p0WoZ6jeHhrD7g==" 304 | }, 305 | "@esbuild/linux-mips64el@0.24.0": { 306 | "integrity": "sha512-hIKvXm0/3w/5+RDtCJeXqMZGkI2s4oMUGj3/jM0QzhgIASWrGO5/RlzAzm5nNh/awHE0A19h/CvHQe6FaBNrRA==" 307 | }, 308 | "@esbuild/linux-ppc64@0.24.0": { 309 | "integrity": "sha512-HcZh5BNq0aC52UoocJxaKORfFODWXZxtBaaZNuN3PUX3MoDsChsZqopzi5UupRhPHSEHotoiptqikjN/B77mYQ==" 310 | }, 311 | "@esbuild/linux-riscv64@0.24.0": { 312 | "integrity": "sha512-bEh7dMn/h3QxeR2KTy1DUszQjUrIHPZKyO6aN1X4BCnhfYhuQqedHaa5MxSQA/06j3GpiIlFGSsy1c7Gf9padw==" 313 | }, 314 | "@esbuild/linux-s390x@0.24.0": { 315 | "integrity": "sha512-ZcQ6+qRkw1UcZGPyrCiHHkmBaj9SiCD8Oqd556HldP+QlpUIe2Wgn3ehQGVoPOvZvtHm8HPx+bH20c9pvbkX3g==" 316 | }, 317 | "@esbuild/linux-x64@0.24.0": { 318 | "integrity": "sha512-vbutsFqQ+foy3wSSbmjBXXIJ6PL3scghJoM8zCL142cGaZKAdCZHyf+Bpu/MmX9zT9Q0zFBVKb36Ma5Fzfa8xA==" 319 | }, 320 | "@esbuild/netbsd-x64@0.24.0": { 321 | "integrity": "sha512-hjQ0R/ulkO8fCYFsG0FZoH+pWgTTDreqpqY7UnQntnaKv95uP5iW3+dChxnx7C3trQQU40S+OgWhUVwCjVFLvg==" 322 | }, 323 | "@esbuild/openbsd-arm64@0.24.0": { 324 | "integrity": "sha512-MD9uzzkPQbYehwcN583yx3Tu5M8EIoTD+tUgKF982WYL9Pf5rKy9ltgD0eUgs8pvKnmizxjXZyLt0z6DC3rRXg==" 325 | }, 326 | "@esbuild/openbsd-x64@0.24.0": { 327 | "integrity": "sha512-4ir0aY1NGUhIC1hdoCzr1+5b43mw99uNwVzhIq1OY3QcEwPDO3B7WNXBzaKY5Nsf1+N11i1eOfFcq+D/gOS15Q==" 328 | }, 329 | "@esbuild/sunos-x64@0.24.0": { 330 | "integrity": "sha512-jVzdzsbM5xrotH+W5f1s+JtUy1UWgjU0Cf4wMvffTB8m6wP5/kx0KiaLHlbJO+dMgtxKV8RQ/JvtlFcdZ1zCPA==" 331 | }, 332 | "@esbuild/win32-arm64@0.24.0": { 333 | "integrity": "sha512-iKc8GAslzRpBytO2/aN3d2yb2z8XTVfNV0PjGlCxKo5SgWmNXx82I/Q3aG1tFfS+A2igVCY97TJ8tnYwpUWLCA==" 334 | }, 335 | "@esbuild/win32-ia32@0.24.0": { 336 | "integrity": "sha512-vQW36KZolfIudCcTnaTpmLQ24Ha1RjygBo39/aLkM2kmjkWmZGEJ5Gn9l5/7tzXA42QGIoWbICfg6KLLkIw6yw==" 337 | }, 338 | "@esbuild/win32-x64@0.24.0": { 339 | "integrity": "sha512-7IAFPrjSQIJrGsK6flwg7NFmwBoSTyF3rl7If0hNUFQU4ilTsEPL6GuMuU9BfIWVVGuRnuIidkSMC+c0Otu8IA==" 340 | }, 341 | "@jridgewell/gen-mapping@0.3.5": { 342 | "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", 343 | "dependencies": [ 344 | "@jridgewell/set-array", 345 | "@jridgewell/sourcemap-codec", 346 | "@jridgewell/trace-mapping" 347 | ] 348 | }, 349 | "@jridgewell/resolve-uri@3.1.2": { 350 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==" 351 | }, 352 | "@jridgewell/set-array@1.2.1": { 353 | "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==" 354 | }, 355 | "@jridgewell/sourcemap-codec@1.5.0": { 356 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==" 357 | }, 358 | "@jridgewell/trace-mapping@0.3.25": { 359 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", 360 | "dependencies": [ 361 | "@jridgewell/resolve-uri", 362 | "@jridgewell/sourcemap-codec" 363 | ] 364 | }, 365 | "@nativescript/foundation@0.0.1-alpha.0": { 366 | "integrity": "sha512-02PY01nBvMVF/xeZS5mKBAXFaoGQ8Zn3N90RDPouo0QGDi3LDsSUqvY1+bI7qOHKnAP+kcuUEQLZDv80pRgT/A==" 367 | }, 368 | "@nativescript/foundation@0.0.1-alpha.19": { 369 | "integrity": "sha512-WE/n01+082h8jIjyWgS0dTUM2iW6ApTXIBEAMc/AKbHwIbaCpMarB1OJ8qbjnGYYUsFLoz3sqjqh4ctiB9HJUw==" 370 | }, 371 | "@nativescript/foundation@0.0.1-alpha.22": { 372 | "integrity": "sha512-WA1kHqnLEpQbEiLQwWS+DjpvykENdqb5AOn78sKS5s8bi1+/2aK7LMBHMzsvrrfZiyfhVvZvWyH69BHK4EBD8A==" 373 | }, 374 | "@nativescript/macos-node-api@0.1.3": { 375 | "integrity": "sha512-RMHZovHP0Rgnxaj1Z26+KrgWmZzHeJmwzVLDCa/UpPmt/vdFZx6ar0swJ/IUKMy4xEWm/f+DuxfBhHNe/USZJQ==", 376 | "dependencies": [ 377 | "@nativescript/objc-node-api" 378 | ] 379 | }, 380 | "@nativescript/objc-node-api@1.0.0-alpha.7": { 381 | "integrity": "sha512-HCkKX46wmc/Oe+Td958HeRmlqKCKmzeeIm0Kofb8aU+6WqjslLK8xGj6HLQT1SyzLWNQdr3dYp7PLFixMjBymw==" 382 | }, 383 | "babel-plugin-jsx-dom-expressions@0.39.3_@babel+core@7.26.0": { 384 | "integrity": "sha512-6RzmSu21zYPlV2gNwzjGG9FgODtt9hIWnx7L//OIioIEuRcnpDZoY8Tr+I81Cy1SrH4qoDyKpwHHo6uAMAeyPA==", 385 | "dependencies": [ 386 | "@babel/core", 387 | "@babel/helper-module-imports@7.18.6", 388 | "@babel/plugin-syntax-jsx", 389 | "@babel/types", 390 | "html-entities", 391 | "parse5", 392 | "validate-html-nesting" 393 | ] 394 | }, 395 | "babel-preset-solid@1.9.3_@babel+core@7.26.0": { 396 | "integrity": "sha512-jvlx5wDp8s+bEF9sGFw/84SInXOA51ttkUEroQziKMbxplXThVKt83qB6bDTa1HuLNatdU9FHpFOiQWs1tLQIg==", 397 | "dependencies": [ 398 | "@babel/core", 399 | "babel-plugin-jsx-dom-expressions" 400 | ] 401 | }, 402 | "browserslist@4.24.2": { 403 | "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", 404 | "dependencies": [ 405 | "caniuse-lite", 406 | "electron-to-chromium", 407 | "node-releases", 408 | "update-browserslist-db" 409 | ] 410 | }, 411 | "caniuse-lite@1.0.30001677": { 412 | "integrity": "sha512-fmfjsOlJUpMWu+mAAtZZZHz7UEwsUxIIvu1TJfO1HqFQvB/B+ii0xr9B5HpbZY/mC4XZ8SvjHJqtAY6pDPQEog==" 413 | }, 414 | "convert-source-map@2.0.0": { 415 | "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==" 416 | }, 417 | "csstype@3.1.3": { 418 | "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" 419 | }, 420 | "debug@4.3.7": { 421 | "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", 422 | "dependencies": [ 423 | "ms" 424 | ] 425 | }, 426 | "electron-to-chromium@1.5.51": { 427 | "integrity": "sha512-kKeWV57KSS8jH4alKt/jKnvHPmJgBxXzGUSbMd4eQF+iOsVPl7bz2KUmu6eo80eMP8wVioTfTyTzdMgM15WXNg==" 428 | }, 429 | "entities@4.5.0": { 430 | "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==" 431 | }, 432 | "esbuild-plugin-solid@0.6.0_esbuild@0.24.0_solid-js@1.9.3__seroval@1.1.1_@babel+core@7.26.0": { 433 | "integrity": "sha512-V1FvDALwLDX6K0XNYM9CMRAnMzA0+Ecu55qBUT9q/eAJh1KIDsTMFoOzMSgyHqbOfvrVfO3Mws3z7TW2GVnIZA==", 434 | "dependencies": [ 435 | "@babel/core", 436 | "@babel/preset-typescript", 437 | "babel-preset-solid", 438 | "esbuild", 439 | "solid-js" 440 | ] 441 | }, 442 | "esbuild@0.24.0": { 443 | "integrity": "sha512-FuLPevChGDshgSicjisSooU0cemp/sGXR841D5LHMB7mTVOmsEHcAxaH3irL53+8YDIeVNQEySh4DaYU/iuPqQ==", 444 | "dependencies": [ 445 | "@esbuild/aix-ppc64", 446 | "@esbuild/android-arm", 447 | "@esbuild/android-arm64", 448 | "@esbuild/android-x64", 449 | "@esbuild/darwin-arm64", 450 | "@esbuild/darwin-x64", 451 | "@esbuild/freebsd-arm64", 452 | "@esbuild/freebsd-x64", 453 | "@esbuild/linux-arm", 454 | "@esbuild/linux-arm64", 455 | "@esbuild/linux-ia32", 456 | "@esbuild/linux-loong64", 457 | "@esbuild/linux-mips64el", 458 | "@esbuild/linux-ppc64", 459 | "@esbuild/linux-riscv64", 460 | "@esbuild/linux-s390x", 461 | "@esbuild/linux-x64", 462 | "@esbuild/netbsd-x64", 463 | "@esbuild/openbsd-arm64", 464 | "@esbuild/openbsd-x64", 465 | "@esbuild/sunos-x64", 466 | "@esbuild/win32-arm64", 467 | "@esbuild/win32-ia32", 468 | "@esbuild/win32-x64" 469 | ] 470 | }, 471 | "escalade@3.2.0": { 472 | "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==" 473 | }, 474 | "gensync@1.0.0-beta.2": { 475 | "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==" 476 | }, 477 | "globals@11.12.0": { 478 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==" 479 | }, 480 | "html-entities@2.3.3": { 481 | "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==" 482 | }, 483 | "js-tokens@4.0.0": { 484 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" 485 | }, 486 | "jsesc@3.0.2": { 487 | "integrity": "sha512-xKqzzWXDttJuOcawBt4KnKHHIf5oQ/Cxax+0PWFG+DFDgHNAdi+TXECADI+RYiFUMmx8792xsMbbgXj4CwnP4g==" 488 | }, 489 | "json5@2.2.3": { 490 | "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==" 491 | }, 492 | "lru-cache@5.1.1": { 493 | "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", 494 | "dependencies": [ 495 | "yallist" 496 | ] 497 | }, 498 | "ms@2.1.3": { 499 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" 500 | }, 501 | "node-releases@2.0.18": { 502 | "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" 503 | }, 504 | "parse5@7.2.0": { 505 | "integrity": "sha512-ZkDsAOcxsUMZ4Lz5fVciOehNcJ+Gb8gTzcA4yl3wnc273BAybYWrQ+Ks/OjCjSEpjvQkDSeZbybK9qj2VHHdGA==", 506 | "dependencies": [ 507 | "entities" 508 | ] 509 | }, 510 | "picocolors@1.1.1": { 511 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" 512 | }, 513 | "semver@6.3.1": { 514 | "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" 515 | }, 516 | "seroval-plugins@1.1.1_seroval@1.1.1": { 517 | "integrity": "sha512-qNSy1+nUj7hsCOon7AO4wdAIo9P0jrzAMp18XhiOzA6/uO5TKtP7ScozVJ8T293oRIvi5wyCHSM4TrJo/c/GJA==", 518 | "dependencies": [ 519 | "seroval" 520 | ] 521 | }, 522 | "seroval@1.1.1": { 523 | "integrity": "sha512-rqEO6FZk8mv7Hyv4UCj3FD3b6Waqft605TLfsCe/BiaylRpyyMC0b+uA5TJKawX3KzMrdi3wsLbCaLplrQmBvQ==" 524 | }, 525 | "solid-js@1.9.3_seroval@1.1.1": { 526 | "integrity": "sha512-5ba3taPoZGt9GY3YlsCB24kCg0Lv/rie/HTD4kG6h4daZZz7+yK02xn8Vx8dLYBc9i6Ps5JwAbEiqjmKaLB3Ag==", 527 | "dependencies": [ 528 | "csstype", 529 | "seroval", 530 | "seroval-plugins" 531 | ] 532 | }, 533 | "update-browserslist-db@1.1.1_browserslist@4.24.2": { 534 | "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", 535 | "dependencies": [ 536 | "browserslist", 537 | "escalade", 538 | "picocolors" 539 | ] 540 | }, 541 | "validate-html-nesting@1.2.2": { 542 | "integrity": "sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==" 543 | }, 544 | "yallist@3.1.1": { 545 | "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" 546 | }, 547 | "yoga-layout@3.1.0": { 548 | "integrity": "sha512-auzJ8lEovThZIpR8wLGWNo/JEj4VTO79q9/gOJ0dWb3shAYPFdX3t9VN0fC0v+jeQF77STUdCzebLwRMqzn5gQ==" 549 | } 550 | } 551 | } 552 | -------------------------------------------------------------------------------- /import_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "@jsx/jsx-runtime": "npm:@nativescript/foundation@0.0.1-alpha.22/jsx/index.d.ts", 4 | "dom": "npm:@nativescript/foundation@0.0.1-alpha.22/dom/index.js", 5 | "app": "./dist/out.js", 6 | "@nativescript/foundation": "npm:@nativescript/foundation@0.0.1-alpha.22", 7 | "@nativescript/macos-node-api": "npm:@nativescript/macos-node-api@~0.1.3", 8 | "solid-native-renderer": "./tools/renderer.js", 9 | "yoga-layout": "npm:yoga-layout@3.1.0" 10 | } 11 | } -------------------------------------------------------------------------------- /main.ts: -------------------------------------------------------------------------------- 1 | import "@nativescript/macos-node-api"; 2 | import { Application } from "@nativescript/foundation"; 3 | 4 | await import("app").then((module) => { 5 | // Async import ensures that objc globals are defined when module is loaded. 6 | module.startApp(); 7 | Application.launch(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/app-menus.tsx: -------------------------------------------------------------------------------- 1 | export default function AppMenus() { 2 | function openDocs() { 3 | NSWorkspace.sharedWorkspace.openURL( 4 | NSURL.URLWithString("https://solidjs.com") 5 | ); 6 | } 7 | 8 | function openGithub() { 9 | NSWorkspace.sharedWorkspace.openURL( 10 | NSURL.URLWithString("https://github.com/solidjs/solid") 11 | ); 12 | } 13 | 14 | function openDiscord() { 15 | NSWorkspace.sharedWorkspace.openURL( 16 | NSURL.URLWithString("https://discord.com/invite/solidjs") 17 | ); 18 | } 19 | 20 | return ( 21 | <> 22 | 23 | { 27 | NSApp.terminate(NSApp); 28 | }} 29 | /> 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /src/app.tsx: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | import { createSignal } from "npm:solid-js"; 4 | import { render } from "solid-native-renderer"; 5 | import AppMenus from "./app-menus.tsx"; 6 | import ContentView from "./contentview.tsx"; 7 | import Sidebar, { type SidebarItem } from "./sidebar.tsx"; 8 | import { selectedView } from "./state.tsx"; 9 | 10 | function App() { 11 | const [currentSidebarItem, setCurrentSidebarItem] = 12 | createSignal(); 13 | return ( 14 | 32 | 33 | 34 | 40 | { 43 | setCurrentSidebarItem(item); 44 | }} 45 | selectedItem={currentSidebarItem()} 46 | /> 47 | 48 | 57 | 58 | 59 | 60 | 61 | ); 62 | } 63 | 64 | /** 65 | * A function export is required here to launch the app 66 | * in the correct context of the native app. This is because the 67 | * solid app needs to be pre-built seperately using esbuild. 68 | * See ./tools/scripts/bundle-solid.ts for more information. 69 | */ 70 | export function startApp() { 71 | render(() => , document.body); 72 | } 73 | -------------------------------------------------------------------------------- /src/components/text.tsx: -------------------------------------------------------------------------------- 1 | function Text() { 2 | return ( 3 | 8 | Hello macOS, ❤️ Solid 9 | 10 | ); 11 | } 12 | Text.code = `function Text() { 13 | return ( 14 | 19 | Hello macOS, ❤️ Solid 20 | 21 | ); 22 | }`; 23 | 24 | export default Text; 25 | -------------------------------------------------------------------------------- /src/contentview.tsx: -------------------------------------------------------------------------------- 1 | import { Component } from "npm:solid-js"; 2 | 3 | interface SnippetProps { 4 | component(): JSX.Element; 5 | } 6 | 7 | const ContentView: Component> = (props) => { 8 | return ( 9 | 17 | {props.component?.()} 18 | 19 | ); 20 | }; 21 | 22 | export default ContentView; 23 | -------------------------------------------------------------------------------- /src/examples/counter.tsx: -------------------------------------------------------------------------------- 1 | import { createSignal } from "npm:solid-js"; 2 | 3 | export function Counter() { 4 | const [count, setCount] = createSignal(0); 5 | 6 | return ( 7 | 14 | 25 | 26 | 31 | {count()} 32 | 33 | 34 | 45 | 46 | ); 47 | } 48 | -------------------------------------------------------------------------------- /src/examples/index.tsx: -------------------------------------------------------------------------------- 1 | import { getSolidLogo } from "../pages/common.ts"; 2 | 3 | export default function Examples() { 4 | return ( 5 | 11 | 18 | 26 | 27 | 33 | Solid Desktop Examples 34 | 35 | 36 | 45 | 51 | Play with the examples to learn how to build macOS apps with Solid and 52 | NativeScript. 53 | 54 | 55 | 56 | ); 57 | } 58 | -------------------------------------------------------------------------------- /src/examples/todo.tsx: -------------------------------------------------------------------------------- 1 | import { createSignal } from "npm:solid-js"; 2 | import { For } from "solid-native-renderer"; 3 | import { createRenderEffect } from "npm:solid-js"; 4 | import { useColorScheme } from "../hooks/use-color-scheme.ts"; 5 | 6 | const storageApi = { 7 | set(key: string, value: string) { 8 | NSUserDefaults.standardUserDefaults.setObjectForKey(value, key); 9 | }, 10 | get(key: string) { 11 | return NSUserDefaults.standardUserDefaults.objectForKey(key); 12 | }, 13 | remove(key: string) { 14 | NSUserDefaults.standardUserDefaults.removeObjectForKey(key); 15 | }, 16 | }; 17 | 18 | type TodoItem = { 19 | id: number; 20 | title: string; 21 | completed: boolean; 22 | }; 23 | 24 | const [todos, setTodos] = createSignal( 25 | JSON.parse(storageApi.get("todos") || "[]") 26 | ); 27 | 28 | export function TodoMVP() { 29 | const [loading, setLoading] = createSignal(true); 30 | // Get system color scheme mode 31 | const colorScheme = useColorScheme(); 32 | let newTodo: string; 33 | let textField: HTMLTextFieldElement | null = null; 34 | 35 | function addTodo() { 36 | setTodos((prev) => { 37 | const next = [...prev]; 38 | next.push({ 39 | id: next.length + 1, 40 | title: newTodo, 41 | completed: false, 42 | }); 43 | storageApi.set("todos", JSON.stringify(next)); 44 | newTodo = ""; 45 | textField?.clear(); 46 | return next; 47 | }); 48 | } 49 | 50 | createRenderEffect(() => { 51 | setTimeout(() => { 52 | setLoading(false); 53 | }, 500); 54 | }); 55 | 56 | return ( 57 | 63 | 72 | 87 | (textField = el)} 89 | onTextChange={(event) => (newTodo = event.value)} 90 | onSubmit={() => addTodo()} 91 | placeholder="Create a new todo item" 92 | style={{ 93 | fontSize: 14, 94 | width: "100%", 95 | flex: 1, 96 | }} 97 | /> 98 | 99 |