a love letter to tangled (android, iOS, and a search API)

build: project scaffold

+421 -13
+6
.prettierrc
··· 1 + { 2 + "tabWidth": 2, 3 + "printWidth": 120, 4 + "objectWrap": "collapse", 5 + "bracketSameLine": true 6 + }
+10 -10
docs/tasks/phase-1.md
··· 2 2 3 3 ## Scaffold 4 4 5 - - [ ] Create Ionic Vue project with TypeScript (`ionic start twisted tabs --type vue`) 6 - - [ ] Configure Capacitor for iOS and Android 7 - - [ ] Set up path aliases (`@/` → `src/`) 8 - - [ ] Install and configure Pinia 9 - - [ ] Install and configure TanStack Query for Vue 10 - - [ ] Create the directory structure per spec (`app/`, `core/`, `services/`, `domain/`, `features/`, `components/`) 5 + - [x] Create Ionic Vue project with TypeScript (`ionic start twisted tabs --type vue`) 6 + - [x] Configure Capacitor for iOS and Android 7 + - [x] Set up path aliases (`@/` → `src/`) 8 + - [x] Install and configure Pinia 9 + - [x] Install and configure TanStack Query for Vue 10 + - [x] Create the directory structure per spec (`app/`, `core/`, `services/`, `domain/`, `features/`, `components/`) 11 11 12 12 ## Routing & Navigation 13 13 14 - - [ ] Define five-tab layout: Home, Explore, Activity, Profile (visible tabs) + Repo (pushed route) 15 - - [ ] Configure Vue Router with Ionic tab routing 16 - - [ ] Add route definitions for all Phase 1 placeholder pages 14 + - [x] Define five-tab layout: Home, Explore, Activity, Profile (visible tabs) + Repo (pushed route) 15 + - [x] Configure Vue Router with Ionic tab routing 16 + - [x] Add route definitions for all Phase 1 placeholder pages 17 17 - [ ] Verify tab-to-tab navigation preserves scroll position and component state 18 18 19 19 ## Domain Models ··· 26 26 27 27 ## Mock Data 28 28 29 + - [ ] Use realistic data: fetch `desertthunder.dev` to create mock data for repo names, timestamps within last 30 days 29 30 - [ ] Create `src/mocks/users.ts` — factory for `UserSummary` instances 30 31 - [ ] Create `src/mocks/repos.ts` — factory for `RepoSummary` and `RepoDetail` instances 31 32 - [ ] Create `src/mocks/pull-requests.ts` — factory for `PullRequestSummary` instances 32 33 - [ ] Create `src/mocks/issues.ts` — factory for `IssueSummary` instances 33 34 - [ ] Create `src/mocks/activity.ts` — factory for `ActivityItem` instances 34 - - [ ] Use realistic data: handles like `alice.tngl.sh`, repo names, timestamps within last 30 days 35 35 36 36 ## Design System Components 37 37
+4 -1
package.json
··· 1 1 { 2 - "name": "Twisted", 2 + "name": "twisted", 3 3 "private": true, 4 4 "version": "0.0.1", 5 5 "type": "module", ··· 21 21 "@capacitor/status-bar": "8.0.1", 22 22 "@ionic/vue": "^8.0.0", 23 23 "@ionic/vue-router": "^8.0.0", 24 + "@tanstack/vue-query": "^5.94.5", 24 25 "ionicons": "^7.0.0", 26 + "pinia": "^3.0.4", 25 27 "vue": "^3.3.0", 26 28 "vue-router": "^4.2.0" 27 29 }, ··· 35 37 "eslint": "^8.35.0", 36 38 "eslint-plugin-vue": "^9.9.0", 37 39 "jsdom": "^22.1.0", 40 + "prettier": "^3.8.1", 38 41 "terser": "^5.4.0", 39 42 "typescript": "~5.9.0", 40 43 "vite": "^5.0.0",
+157
pnpm-lock.yaml
··· 35 35 '@ionic/vue-router': 36 36 specifier: ^8.0.0 37 37 version: 8.8.1(@stencil/core@4.43.3)(vue-router@4.6.4(vue@3.5.30(typescript@5.9.3)))(vue@3.5.30(typescript@5.9.3)) 38 + '@tanstack/vue-query': 39 + specifier: ^5.94.5 40 + version: 5.94.5(vue@3.5.30(typescript@5.9.3)) 38 41 ionicons: 39 42 specifier: ^7.0.0 40 43 version: 7.4.0 44 + pinia: 45 + specifier: ^3.0.4 46 + version: 3.0.4(typescript@5.9.3)(vue@3.5.30(typescript@5.9.3)) 41 47 vue: 42 48 specifier: ^3.3.0 43 49 version: 3.5.30(typescript@5.9.3) ··· 72 78 jsdom: 73 79 specifier: ^22.1.0 74 80 version: 22.1.0 81 + prettier: 82 + specifier: ^3.8.1 83 + version: 3.8.1 75 84 terser: 76 85 specifier: ^5.4.0 77 86 version: 5.46.1 ··· 1139 1148 vue-router: 1140 1149 optional: true 1141 1150 1151 + '@tanstack/match-sorter-utils@8.19.4': 1152 + resolution: {integrity: sha512-Wo1iKt2b9OT7d+YGhvEPD3DXvPv2etTusIMhMUoG7fbhmxcXCtIjJDEygy91Y2JFlwGyjqiBPRozme7UD8hoqg==} 1153 + engines: {node: '>=12'} 1154 + 1155 + '@tanstack/query-core@5.94.5': 1156 + resolution: {integrity: sha512-Vx1JJiBURW/wdNGP45afjrqn0LfxYwL7K/bSrQvNRtyLGF1bxQPgUXCpzscG29e+UeFOh9hz1KOVala0N+bZiA==} 1157 + 1158 + '@tanstack/vue-query@5.94.5': 1159 + resolution: {integrity: sha512-xmnOj1fP0JvUqGrkHmdIY/3FyO4L0IjJBCqOFxnnMIJjrsvCvlpjp/XpI1Zv4eLuV0e8l1LIOOuEvN40ckVuOA==} 1160 + peerDependencies: 1161 + '@vue/composition-api': ^1.1.2 1162 + vue: ^2.6.0 || ^3.3.0 1163 + peerDependenciesMeta: 1164 + '@vue/composition-api': 1165 + optional: true 1166 + 1142 1167 '@tootallnate/once@2.0.0': 1143 1168 resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} 1144 1169 engines: {node: '>= 10'} ··· 1295 1320 '@vue/devtools-api@6.6.4': 1296 1321 resolution: {integrity: sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==} 1297 1322 1323 + '@vue/devtools-api@7.7.9': 1324 + resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==} 1325 + 1326 + '@vue/devtools-kit@7.7.9': 1327 + resolution: {integrity: sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA==} 1328 + 1329 + '@vue/devtools-shared@7.7.9': 1330 + resolution: {integrity: sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA==} 1331 + 1298 1332 '@vue/eslint-config-typescript@12.0.0': 1299 1333 resolution: {integrity: sha512-StxLFet2Qe97T8+7L8pGlhYBBr8Eg05LPuTDVopQV6il+SK6qqom59BA/rcFipUef2jD8P2X44Vd8tMFytfvlg==} 1300 1334 engines: {node: ^14.17.0 || >=16.0.0} ··· 1479 1513 resolution: {integrity: sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==} 1480 1514 engines: {node: '>=0.6'} 1481 1515 1516 + birpc@2.9.0: 1517 + resolution: {integrity: sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw==} 1518 + 1482 1519 blob-util@2.0.2: 1483 1520 resolution: {integrity: sha512-T7JQa+zsXXEa6/8ZhHcQEW1UFfVM49Ts65uBkFL6fz2QmrElqmbajIDJvuA0tEhRe5eIjpV9ZF+0RfZR9voJFQ==} 1484 1521 ··· 1637 1674 convert-source-map@2.0.0: 1638 1675 resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} 1639 1676 1677 + copy-anything@4.0.5: 1678 + resolution: {integrity: sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA==} 1679 + engines: {node: '>=18'} 1680 + 1640 1681 core-js-compat@3.49.0: 1641 1682 resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} 1642 1683 ··· 2042 2083 resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} 2043 2084 hasBin: true 2044 2085 2086 + hookable@5.5.3: 2087 + resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==} 2088 + 2045 2089 html-encoding-sniffer@3.0.0: 2046 2090 resolution: {integrity: sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==} 2047 2091 engines: {node: '>=12'} ··· 2155 2199 is-unicode-supported@0.1.0: 2156 2200 resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} 2157 2201 engines: {node: '>=10'} 2202 + 2203 + is-what@5.5.0: 2204 + resolution: {integrity: sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw==} 2205 + engines: {node: '>=18'} 2158 2206 2159 2207 is-wsl@2.2.0: 2160 2208 resolution: {integrity: sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==} ··· 2358 2406 resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==} 2359 2407 engines: {node: '>= 18'} 2360 2408 2409 + mitt@3.0.1: 2410 + resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==} 2411 + 2361 2412 mlly@1.8.2: 2362 2413 resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} 2363 2414 ··· 2488 2539 pend@1.2.0: 2489 2540 resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} 2490 2541 2542 + perfect-debounce@1.0.0: 2543 + resolution: {integrity: sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==} 2544 + 2491 2545 performance-now@2.1.0: 2492 2546 resolution: {integrity: sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==} 2493 2547 ··· 2502 2556 resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} 2503 2557 engines: {node: '>=0.10.0'} 2504 2558 2559 + pinia@3.0.4: 2560 + resolution: {integrity: sha512-l7pqLUFTI/+ESXn6k3nu30ZIzW5E2WZF/LaHJEpoq6ElcLD+wduZoB2kBN19du6K/4FDpPMazY2wJr+IndBtQw==} 2561 + peerDependencies: 2562 + typescript: '>=4.5.0' 2563 + vue: ^3.5.11 2564 + peerDependenciesMeta: 2565 + typescript: 2566 + optional: true 2567 + 2505 2568 pkg-types@1.3.1: 2506 2569 resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} 2507 2570 ··· 2520 2583 prelude-ls@1.2.1: 2521 2584 resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} 2522 2585 engines: {node: '>= 0.8.0'} 2586 + 2587 + prettier@3.8.1: 2588 + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} 2589 + engines: {node: '>=14'} 2590 + hasBin: true 2523 2591 2524 2592 pretty-bytes@5.6.0: 2525 2593 resolution: {integrity: sha512-FFw039TmrBqFK8ma/7OL3sDz/VytdtJr044/QUJtH0wK9lb9jLq9tJyIxUwtQJHwar2BqtiA4iCWSwo9JLkzFg==} ··· 2591 2659 resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==} 2592 2660 hasBin: true 2593 2661 2662 + remove-accents@0.5.0: 2663 + resolution: {integrity: sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==} 2664 + 2594 2665 request-progress@3.0.0: 2595 2666 resolution: {integrity: sha512-MnWzEHHaxHO2iWiQuHrUPBi/1WeBf5PkxQqNyNvLl9VAYSdXkP8tQ3pBSeCPD+yw0v0Aq1zosWLz0BdeXpWwZg==} 2596 2667 ··· 2727 2798 resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 2728 2799 engines: {node: '>=0.10.0'} 2729 2800 2801 + speakingurl@14.0.1: 2802 + resolution: {integrity: sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==} 2803 + engines: {node: '>=0.10.0'} 2804 + 2730 2805 split2@4.2.0: 2731 2806 resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} 2732 2807 engines: {node: '>= 10.x'} ··· 2771 2846 2772 2847 strip-literal@1.3.0: 2773 2848 resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} 2849 + 2850 + superjson@2.2.6: 2851 + resolution: {integrity: sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA==} 2852 + engines: {node: '>=16'} 2774 2853 2775 2854 supports-color@7.2.0: 2776 2855 resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} ··· 3022 3101 vue-component-type-helpers@2.2.12: 3023 3102 resolution: {integrity: sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==} 3024 3103 3104 + vue-demi@0.14.10: 3105 + resolution: {integrity: sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==} 3106 + engines: {node: '>=12'} 3107 + hasBin: true 3108 + peerDependencies: 3109 + '@vue/composition-api': ^1.0.0-rc.1 3110 + vue: ^3.0.0-0 || ^2.6.0 3111 + peerDependenciesMeta: 3112 + '@vue/composition-api': 3113 + optional: true 3114 + 3025 3115 vue-eslint-parser@9.4.3: 3026 3116 resolution: {integrity: sha512-2rYRLWlIpaiN8xbPiDyXZXRgLGOtWxERV7ND5fFAv5qo1D2N9Fu9MNajBNc6o13lZ+24DAWCkQCvj4klgmcITg==} 3027 3117 engines: {node: ^14.17.0 || >=16.0.0} ··· 4325 4415 '@stencil/core': 4.43.3 4326 4416 vue-router: 4.6.4(vue@3.5.30(typescript@5.9.3)) 4327 4417 4418 + '@tanstack/match-sorter-utils@8.19.4': 4419 + dependencies: 4420 + remove-accents: 0.5.0 4421 + 4422 + '@tanstack/query-core@5.94.5': {} 4423 + 4424 + '@tanstack/vue-query@5.94.5(vue@3.5.30(typescript@5.9.3))': 4425 + dependencies: 4426 + '@tanstack/match-sorter-utils': 8.19.4 4427 + '@tanstack/query-core': 5.94.5 4428 + '@vue/devtools-api': 6.6.4 4429 + vue: 3.5.30(typescript@5.9.3) 4430 + vue-demi: 0.14.10(vue@3.5.30(typescript@5.9.3)) 4431 + 4328 4432 '@tootallnate/once@2.0.0': {} 4329 4433 4330 4434 '@types/chai-subset@1.3.6(@types/chai@4.3.20)': ··· 4543 4647 4544 4648 '@vue/devtools-api@6.6.4': {} 4545 4649 4650 + '@vue/devtools-api@7.7.9': 4651 + dependencies: 4652 + '@vue/devtools-kit': 7.7.9 4653 + 4654 + '@vue/devtools-kit@7.7.9': 4655 + dependencies: 4656 + '@vue/devtools-shared': 7.7.9 4657 + birpc: 2.9.0 4658 + hookable: 5.5.3 4659 + mitt: 3.0.1 4660 + perfect-debounce: 1.0.0 4661 + speakingurl: 14.0.1 4662 + superjson: 2.2.6 4663 + 4664 + '@vue/devtools-shared@7.7.9': 4665 + dependencies: 4666 + rfdc: 1.4.1 4667 + 4546 4668 '@vue/eslint-config-typescript@12.0.0(eslint-plugin-vue@9.33.0(eslint@8.57.1))(eslint@8.57.1)(typescript@5.9.3)': 4547 4669 dependencies: 4548 4670 '@typescript-eslint/eslint-plugin': 6.21.0(@typescript-eslint/parser@6.21.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) ··· 4715 4837 4716 4838 big-integer@1.6.52: {} 4717 4839 4840 + birpc@2.9.0: {} 4841 + 4718 4842 blob-util@2.0.2: {} 4719 4843 4720 4844 bluebird@3.7.2: {} ··· 4858 4982 proto-list: 1.2.4 4859 4983 4860 4984 convert-source-map@2.0.0: {} 4985 + 4986 + copy-anything@4.0.5: 4987 + dependencies: 4988 + is-what: 5.5.0 4861 4989 4862 4990 core-js-compat@3.49.0: 4863 4991 dependencies: ··· 5374 5502 5375 5503 he@1.2.0: {} 5376 5504 5505 + hookable@5.5.3: {} 5506 + 5377 5507 html-encoding-sniffer@3.0.0: 5378 5508 dependencies: 5379 5509 whatwg-encoding: 2.0.0 ··· 5469 5599 is-typedarray@1.0.0: {} 5470 5600 5471 5601 is-unicode-supported@0.1.0: {} 5602 + 5603 + is-what@5.5.0: {} 5472 5604 5473 5605 is-wsl@2.2.0: 5474 5606 dependencies: ··· 5674 5806 dependencies: 5675 5807 minipass: 7.1.3 5676 5808 5809 + mitt@3.0.1: {} 5810 + 5677 5811 mlly@1.8.2: 5678 5812 dependencies: 5679 5813 acorn: 8.16.0 ··· 5804 5938 5805 5939 pend@1.2.0: {} 5806 5940 5941 + perfect-debounce@1.0.0: {} 5942 + 5807 5943 performance-now@2.1.0: {} 5808 5944 5809 5945 picocolors@1.1.1: {} ··· 5812 5948 5813 5949 pify@2.3.0: {} 5814 5950 5951 + pinia@3.0.4(typescript@5.9.3)(vue@3.5.30(typescript@5.9.3)): 5952 + dependencies: 5953 + '@vue/devtools-api': 7.7.9 5954 + vue: 3.5.30(typescript@5.9.3) 5955 + optionalDependencies: 5956 + typescript: 5.9.3 5957 + 5815 5958 pkg-types@1.3.1: 5816 5959 dependencies: 5817 5960 confbox: 0.1.8 ··· 5836 5979 source-map-js: 1.2.1 5837 5980 5838 5981 prelude-ls@1.2.1: {} 5982 + 5983 + prettier@3.8.1: {} 5839 5984 5840 5985 pretty-bytes@5.6.0: {} 5841 5986 ··· 5905 6050 regjsparser@0.13.0: 5906 6051 dependencies: 5907 6052 jsesc: 3.1.0 6053 + 6054 + remove-accents@0.5.0: {} 5908 6055 5909 6056 request-progress@3.0.0: 5910 6057 dependencies: ··· 6060 6207 6061 6208 source-map@0.6.1: {} 6062 6209 6210 + speakingurl@14.0.1: {} 6211 + 6063 6212 split2@4.2.0: {} 6064 6213 6065 6214 sshpk@1.18.0: ··· 6109 6258 strip-literal@1.3.0: 6110 6259 dependencies: 6111 6260 acorn: 8.16.0 6261 + 6262 + superjson@2.2.6: 6263 + dependencies: 6264 + copy-anything: 4.0.5 6112 6265 6113 6266 supports-color@7.2.0: 6114 6267 dependencies: ··· 6326 6479 vscode-uri@3.1.0: {} 6327 6480 6328 6481 vue-component-type-helpers@2.2.12: {} 6482 + 6483 + vue-demi@0.14.10(vue@3.5.30(typescript@5.9.3)): 6484 + dependencies: 6485 + vue: 3.5.30(typescript@5.9.3) 6329 6486 6330 6487 vue-eslint-parser@9.4.3(eslint@8.57.1): 6331 6488 dependencies:
+1
pnpm-workspace.yaml
··· 2 2 - core-js 3 3 - cypress 4 4 - esbuild 5 + - vue-demi
src/app/boot/.gitkeep

This is a binary file and will not be displayed.

src/app/providers/.gitkeep

This is a binary file and will not be displayed.

+76
src/app/router/index.ts
··· 1 + import { createRouter, createWebHistory } from '@ionic/vue-router'; 2 + import type { RouteRecordRaw } from 'vue-router'; 3 + 4 + import TabsPage from '@/views/TabsPage.vue'; 5 + 6 + const routes: RouteRecordRaw[] = [ 7 + { 8 + path: '/', 9 + redirect: '/tabs/home', 10 + }, 11 + { 12 + path: '/tabs/', 13 + component: TabsPage, 14 + children: [ 15 + { 16 + path: '', 17 + redirect: '/tabs/home', 18 + }, 19 + { 20 + path: 'home', 21 + children: [ 22 + { 23 + path: '', 24 + component: () => import('@/features/home/HomePage.vue'), 25 + }, 26 + { 27 + path: 'repo/:owner/:repo', 28 + component: () => import('@/features/repo/RepoDetailPage.vue'), 29 + }, 30 + ], 31 + }, 32 + { 33 + path: 'explore', 34 + children: [ 35 + { 36 + path: '', 37 + component: () => import('@/features/explore/ExplorePage.vue'), 38 + }, 39 + { 40 + path: 'repo/:owner/:repo', 41 + component: () => import('@/features/repo/RepoDetailPage.vue'), 42 + }, 43 + ], 44 + }, 45 + { 46 + path: 'activity', 47 + children: [ 48 + { 49 + path: '', 50 + component: () => import('@/features/activity/ActivityPage.vue'), 51 + }, 52 + { 53 + path: 'repo/:owner/:repo', 54 + component: () => import('@/features/repo/RepoDetailPage.vue'), 55 + }, 56 + ], 57 + }, 58 + { 59 + path: 'profile', 60 + children: [ 61 + { 62 + path: '', 63 + component: () => import('@/features/profile/ProfilePage.vue'), 64 + }, 65 + ], 66 + }, 67 + ], 68 + }, 69 + ]; 70 + 71 + const router = createRouter({ 72 + history: createWebHistory(import.meta.env.BASE_URL), 73 + routes, 74 + }); 75 + 76 + export default router;
src/components/common/.gitkeep

This is a binary file and will not be displayed.

src/components/feed/.gitkeep

This is a binary file and will not be displayed.

src/components/profile/.gitkeep

This is a binary file and will not be displayed.

src/components/repo/.gitkeep

This is a binary file and will not be displayed.

src/core/auth/.gitkeep

This is a binary file and will not be displayed.

src/core/config/.gitkeep

This is a binary file and will not be displayed.

src/core/errors/.gitkeep

This is a binary file and will not be displayed.

+11
src/core/query/client.ts
··· 1 + import { QueryClient } from '@tanstack/vue-query'; 2 + 3 + export const queryClient = new QueryClient({ 4 + defaultOptions: { 5 + queries: { 6 + staleTime: 5 * 60 * 1000, // 5 minutes 7 + gcTime: 10 * 60 * 1000, // 10 minutes 8 + retry: 2, 9 + }, 10 + }, 11 + });
src/core/storage/.gitkeep

This is a binary file and will not be displayed.

src/domain/feed/.gitkeep

This is a binary file and will not be displayed.

src/domain/models/.gitkeep

This is a binary file and will not be displayed.

src/domain/profile/.gitkeep

This is a binary file and will not be displayed.

src/domain/repo/.gitkeep

This is a binary file and will not be displayed.

+20
src/features/activity/ActivityPage.vue
··· 1 + <template> 2 + <ion-page> 3 + <ion-header :translucent="true"> 4 + <ion-toolbar> 5 + <ion-title>Activity</ion-title> 6 + </ion-toolbar> 7 + </ion-header> 8 + <ion-content :fullscreen="true"> 9 + <ion-header collapse="condense"> 10 + <ion-toolbar> 11 + <ion-title size="large">Activity</ion-title> 12 + </ion-toolbar> 13 + </ion-header> 14 + </ion-content> 15 + </ion-page> 16 + </template> 17 + 18 + <script setup lang="ts"> 19 + import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue'; 20 + </script>
+20
src/features/explore/ExplorePage.vue
··· 1 + <template> 2 + <ion-page> 3 + <ion-header :translucent="true"> 4 + <ion-toolbar> 5 + <ion-title>Explore</ion-title> 6 + </ion-toolbar> 7 + </ion-header> 8 + <ion-content :fullscreen="true"> 9 + <ion-header collapse="condense"> 10 + <ion-toolbar> 11 + <ion-title size="large">Explore</ion-title> 12 + </ion-toolbar> 13 + </ion-header> 14 + </ion-content> 15 + </ion-page> 16 + </template> 17 + 18 + <script setup lang="ts"> 19 + import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue'; 20 + </script>
+20
src/features/home/HomePage.vue
··· 1 + <template> 2 + <ion-page> 3 + <ion-header :translucent="true"> 4 + <ion-toolbar> 5 + <ion-title>Home</ion-title> 6 + </ion-toolbar> 7 + </ion-header> 8 + <ion-content :fullscreen="true"> 9 + <ion-header collapse="condense"> 10 + <ion-toolbar> 11 + <ion-title size="large">Home</ion-title> 12 + </ion-toolbar> 13 + </ion-header> 14 + </ion-content> 15 + </ion-page> 16 + </template> 17 + 18 + <script setup lang="ts"> 19 + import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue'; 20 + </script>
+20
src/features/profile/ProfilePage.vue
··· 1 + <template> 2 + <ion-page> 3 + <ion-header :translucent="true"> 4 + <ion-toolbar> 5 + <ion-title>Profile</ion-title> 6 + </ion-toolbar> 7 + </ion-header> 8 + <ion-content :fullscreen="true"> 9 + <ion-header collapse="condense"> 10 + <ion-toolbar> 11 + <ion-title size="large">Profile</ion-title> 12 + </ion-toolbar> 13 + </ion-header> 14 + </ion-content> 15 + </ion-page> 16 + </template> 17 + 18 + <script setup lang="ts"> 19 + import { IonContent, IonHeader, IonPage, IonTitle, IonToolbar } from '@ionic/vue'; 20 + </script>
+31
src/features/repo/RepoDetailPage.vue
··· 1 + <template> 2 + <ion-page> 3 + <ion-header :translucent="true"> 4 + <ion-toolbar> 5 + <ion-buttons slot="start"> 6 + <ion-back-button default-href="/tabs/home" /> 7 + </ion-buttons> 8 + <ion-title>{{ owner }}/{{ repo }}</ion-title> 9 + </ion-toolbar> 10 + </ion-header> 11 + <ion-content :fullscreen="true"> 12 + </ion-content> 13 + </ion-page> 14 + </template> 15 + 16 + <script setup lang="ts"> 17 + import { 18 + IonPage, 19 + IonHeader, 20 + IonToolbar, 21 + IonTitle, 22 + IonContent, 23 + IonButtons, 24 + IonBackButton, 25 + } from '@ionic/vue'; 26 + import { useRoute } from 'vue-router'; 27 + 28 + const route = useRoute(); 29 + const owner = route.params.owner as string; 30 + const repo = route.params.repo as string; 31 + </script>
+7 -2
src/main.ts
··· 1 1 import { createApp } from 'vue' 2 2 import App from './App.vue' 3 - import router from './router'; 3 + import router from './app/router'; 4 4 5 5 import { IonicVue } from '@ionic/vue'; 6 + import { createPinia } from 'pinia'; 7 + import { VueQueryPlugin } from '@tanstack/vue-query'; 8 + import { queryClient } from './core/query/client'; 6 9 7 10 /* Core CSS required for Ionic components to work properly */ 8 11 import '@ionic/vue/css/core.css'; ··· 36 39 37 40 const app = createApp(App) 38 41 .use(IonicVue) 39 - .use(router); 42 + .use(router) 43 + .use(createPinia()) 44 + .use(VueQueryPlugin, { queryClient }); 40 45 41 46 router.isReady().then(() => { 42 47 app.mount('#app');
src/mocks/.gitkeep

This is a binary file and will not be displayed.

src/services/atproto/.gitkeep

This is a binary file and will not be displayed.

src/services/tangled/.gitkeep

This is a binary file and will not be displayed.

+38
src/views/TabsPage.vue
··· 1 + <template> 2 + <ion-page> 3 + <ion-tabs> 4 + <ion-router-outlet /> 5 + <ion-tab-bar slot="bottom"> 6 + <ion-tab-button tab="home" href="/tabs/home"> 7 + <ion-icon :icon="homeOutline" /> 8 + <ion-label>Home</ion-label> 9 + </ion-tab-button> 10 + <ion-tab-button tab="explore" href="/tabs/explore"> 11 + <ion-icon :icon="searchOutline" /> 12 + <ion-label>Explore</ion-label> 13 + </ion-tab-button> 14 + <ion-tab-button tab="activity" href="/tabs/activity"> 15 + <ion-icon :icon="pulseOutline" /> 16 + <ion-label>Activity</ion-label> 17 + </ion-tab-button> 18 + <ion-tab-button tab="profile" href="/tabs/profile"> 19 + <ion-icon :icon="personOutline" /> 20 + <ion-label>Profile</ion-label> 21 + </ion-tab-button> 22 + </ion-tab-bar> 23 + </ion-tabs> 24 + </ion-page> 25 + </template> 26 + 27 + <script setup lang="ts"> 28 + import { 29 + IonPage, 30 + IonTabs, 31 + IonRouterOutlet, 32 + IonTabBar, 33 + IonTabButton, 34 + IonIcon, 35 + IonLabel, 36 + } from '@ionic/vue'; 37 + import { homeOutline, searchOutline, pulseOutline, personOutline } from 'ionicons/icons'; 38 + </script>