Compare commits

..

196 Commits

Author SHA1 Message Date
Guido D'Orsi
40e1ca7cb1 Merge pull request #2606 from garden-co/changeset-release/main
Version Packages
2025-07-07 11:30:02 +02:00
github-actions[bot]
80cf21e453 Version Packages 2025-07-07 09:27:27 +00:00
Guido D'Orsi
48c8a3d219 Merge pull request #2603 from garden-co/PR-template-v2-simplify
simplify PR template for ease of use
2025-07-07 11:25:24 +02:00
Guido D'Orsi
31bb1201fc Merge pull request #2611 from garden-co/gio/update-lockfile
chore: update lockfile
2025-07-07 11:15:25 +02:00
Giordano Ricci
08d1b05607 chore: update lockfile 2025-07-07 09:47:51 +01:00
Guido D'Orsi
d64a14210d Merge pull request #2608 from jeffgca/main
Error: Loading PostCSS Plugin failed: Cannot find module '@tailwindcss/postcss'
2025-07-07 10:35:06 +02:00
Guido D'Orsi
7e53d33e9b Merge pull request #2609 from jeffgca/user_age_calc_fix
User age calc fix
2025-07-07 10:34:31 +02:00
Jeff Griffiths
ea2b39cc30 fixed off-by-one error 2025-07-04 21:27:54 -07:00
Jeff Griffiths
6b835f95cf enhanced getUserAge to calculate the user's age in a more precise way. 2025-07-04 21:14:32 -07:00
Jeff Griffiths
a229ae5f70 changed postcss dependency to the tailwind plugin instead. 2025-07-04 20:49:46 -07:00
Giordano Ricci
84fdc1d8fd Merge pull request #2605 from garden-co/gio/cancel-pending-workflows-on-push 2025-07-04 17:19:27 +01:00
Giordano Ricci
14a8b32522 differentiate workflows 2025-07-04 17:10:44 +01:00
Guido D'Orsi
ddc09a0d6b Merge pull request #2604 from garden-co/gio/get-only-direct-members
feat: allow to get only the direct members of a group
2025-07-04 18:09:55 +02:00
Giordano Ricci
3b45a3f2fd chore: cancel pending workflows on push 2025-07-04 17:05:45 +01:00
Giordano Ricci
9034a45da0 forgot the role 2025-07-04 16:51:07 +01:00
Giordano Ricci
a5ceaffb0c changeset, usemethod instead of getter, reuse logic 2025-07-04 16:47:19 +01:00
Giordano Ricci
dcee2f9b4e better test 2025-07-04 16:18:53 +01:00
Giordano Ricci
83fdc504ff feat: add directMembers get to get only the direct members of a given group 2025-07-04 16:07:30 +01:00
Guido D'Orsi
a34c0675cd Merge pull request #2599 from garden-co/changeset-release/main
Version Packages
2025-07-04 14:36:26 +02:00
Margaret Culotta
5a8a62b4a3 simplify PR template for ease of use 2025-07-02 13:23:56 -05:00
github-actions[bot]
325a554bd1 Version Packages 2025-07-02 17:07:22 +00:00
Guido D'Orsi
7422943e83 Merge pull request #2600 from garden-co/fix/react-native-peer-dependencies
Make all React Native deps in `jazz-tools` optional peer dependencies
2025-07-02 19:05:22 +02:00
NicoR
23bfea5861 Add changeset 2025-07-02 13:50:45 -03:00
NicoR
605a54eb11 Make react-native-fast-encoder an optional peer dependency 2025-07-02 13:49:04 -03:00
Brad Anderson
a7aaee51e6 Merge pull request #2587 from garden-co/feat/rn-betterauth
feat: add RN BetterAuth
2025-07-02 12:24:38 -04:00
Brad Anderson
4b8983858a chore: changeset 2025-07-02 12:07:25 -04:00
Brad Anderson
8a8c4d11e1 fix: small cleanup 2025-07-02 11:56:40 -04:00
Guido D'Orsi
b774bb345d chore: changeset 2025-07-02 10:52:44 +02:00
Guido D'Orsi
7fd891d7b9 chore: fix formatting 2025-07-02 10:52:10 +02:00
Guido D'Orsi
27cac4a6d7 Merge pull request #2596 from satendra03/main
fix #1914
2025-07-02 10:51:23 +02:00
Brad Anderson
2b71ef1181 fix: PR feedback 2025-07-01 21:46:25 -04:00
NicoR
ae169c7b3a Revert change for react-native-fast-encoder 2025-07-01 13:57:17 -03:00
NicoR
d888c99d9a Add expo-sqlite dependency to Expo Project setup docs 2025-07-01 13:53:01 -03:00
NicoR
0b54917f19 Make all React Native deps in jazz-tools optional peer dependencies 2025-07-01 12:21:38 -03:00
Guido D'Orsi
c87b215b75 Merge pull request #2594 from garden-co/fix-RNQuickCrypto-type-error
fix: `RNQuickCrypto` type error
2025-07-01 17:15:01 +02:00
NicoR
e4ba23cbef Add changeset 2025-07-01 12:13:40 -03:00
Brad Anderson
98c005a6e0 feat: more RN BetterAuth 2025-07-01 08:49:13 -04:00
Guido D'Orsi
8528db4de4 Merge pull request #2595 from garden-co/fix/make-jazz-tools-rn-deps-peer-dependencies
fix: make `react-native-nitro-modules` and `react-native-quick-crypto` optional peer dependencies
2025-07-01 11:30:14 +02:00
satendra03
e0fe5a20b7 fix #1914 2025-07-01 04:21:26 +05:30
NicoR
e16e4d53d1 Keep file extension in relative import 2025-06-30 16:52:09 -03:00
NicoR
d904fae506 fix: make react-native-quick-crypto an optional peer dependency 2025-06-30 15:44:04 -03:00
NicoR
f67c0b3db3 fix: make react-native-nitro-modules an optional peer dependency 2025-06-30 15:43:30 -03:00
NicoR
283d7c6bf0 fix: RNQuickCrypto type error 2025-06-30 15:13:10 -03:00
Guido D'Orsi
0e7a7dbbc0 Merge pull request #2591 from garden-co/fix/ci-e2e-exit-code
fix(ci): listen to e2e-rn-test's exit code
2025-06-30 17:53:34 +02:00
Matteo Manchi
63c69b6b95 fix(ci): listen to e2e-rn-test's exit code 2025-06-30 17:41:57 +02:00
Guido D'Orsi
fd02627069 Merge pull request #2583 from garden-co/changeset-release/main
Version Packages
2025-06-27 18:14:31 +02:00
github-actions[bot]
827adc991d Version Packages 2025-06-27 16:01:01 +00:00
Guido D'Orsi
651b69e5af Merge pull request #2582 from garden-co/fix/circular-deps-sync
fix: fix sync with circular deps
2025-06-27 17:57:32 +02:00
Guido D'Orsi
277e4d49e8 fix: fix sync with circular deps 2025-06-27 17:52:02 +02:00
Guido D'Orsi
cfa44f32eb Merge pull request #2578 from garden-co/changeset-release/main
Version Packages
2025-06-27 12:53:43 +02:00
github-actions[bot]
ef920435e9 Version Packages 2025-06-27 10:07:38 +00:00
Guido D'Orsi
87d05404dd Merge pull request #2581 from garden-co/fix/idbtransaction
fix: refresh the IndexedDB transaction when finished but not flagged as done
2025-06-27 12:03:19 +02:00
Guido D'Orsi
535c460f5a fix: refresh the IndexedDB transaction when finished but not flagged as done 2025-06-27 11:49:49 +02:00
Guido D'Orsi
fa1b302474 Merge pull request #2577 from garden-co/fix/GCO-600-image-original-size
Fix GCO-600: image original size
2025-06-26 14:35:27 +02:00
Matteo Manchi
45f73a774c chore: update changeset 2025-06-26 12:08:55 +02:00
Matteo Manchi
2a9e271dc3 chore(create-jazz-app/browser-media-images): small refactoring of function 2025-06-26 12:04:51 +02:00
Anselm Eickhoff
3d96d9c829 Merge pull request #2416 from garden-co/feat/design-system-improvements
Feat/design system improvements
2025-06-26 10:16:24 +01:00
Anselm
844051405d Smaller nav CTA & move CTA to menu on mobile 2025-06-26 10:07:24 +01:00
Anselm
625eff2333 Fix pricing page button 2025-06-26 09:54:07 +01:00
Anselm
59e2871065 Rename styleType to intent 2025-06-26 09:49:26 +01:00
Anselm
acdc88fb91 Tweak md icon size 2025-06-26 09:47:53 +01:00
Matteo Manchi
05eab4e2a9 fix(jazz-tools/browser-media-images): use coherent values for resized images - Fixes GCO-600 2025-06-25 23:43:13 +02:00
Guido D'Orsi
efcd65ae38 docs: improve the contributing 2025-06-25 15:42:39 +02:00
Meg Culotta
ad60fa942a Merge pull request #2571 from garden-co/closes-gco-581-add-pr-template-to-gh
Closes GCO-581 - Create pull_request_template.md
2025-06-25 08:05:52 -05:00
Sammii
5272d3cd2a update nav button on gcmp homepage 2025-06-24 16:10:04 +01:00
Sammii
d837811813 variant updates on fake get started buttons and example link components 2025-06-24 15:54:18 +01:00
Guido D'Orsi
2b4aba2d1b fix: fix todo schema migration 2025-06-24 16:53:13 +02:00
Sammii
50b4da18d9 refactoring everything to be more compatible with shadcn nomenclature, styleVariants now variants, variants now styleTyles, shad cn button variants mapped to design system, all buttons/icons etc required are updated monorepo wide 2025-06-24 15:41:41 +01:00
Sammii
df22f2617e amend secondary button style to default 2025-06-24 12:01:33 +01:00
Sammii
280495c533 updating styles on HelpLinks and NewsLetterForm 2025-06-24 11:40:46 +01:00
Sammii
d5c6fbdc3c removing secondary styles 2025-06-24 11:40:32 +01:00
Sammii
57776a1400 amending default button with icon styling 2025-06-24 11:40:24 +01:00
Sammii
156c45aa0e update button styles on design system side nav 2025-06-24 11:28:34 +01:00
Sammii
2d0dba6bbc amend button style on early adopter section 2025-06-24 11:23:44 +01:00
Sammii
7241d2ad95 refactor button highlight to strong 2025-06-24 11:22:46 +01:00
Sammii
4a9eeace00 refactor highlight to strong colours inputs and icons 2025-06-24 11:21:32 +01:00
Meg Culotta
4f9c91f6ff Closes GCO-581 - Create pull_request_template.md 2025-06-23 14:39:52 -05:00
Sammii
7ff13a8f55 erfactor out secondary variant 2025-06-23 16:52:51 +01:00
Sammii
5741d7f09c update input props table 2025-06-23 14:34:36 +01:00
Sammii
766d2c8846 update inputs view 2025-06-23 14:33:02 +01:00
Sammii
70a43d0c39 Icon refactor and style adjust 2025-06-23 14:26:39 +01:00
Sammii
cf44258848 variant fix 2025-06-23 11:41:30 +01:00
Sammii
4db5ec2dd8 variant update ViewsSideMenu 2025-06-23 11:39:12 +01:00
Sammii
4983e57e62 more styling updates 2025-06-23 11:21:22 +01:00
Sammii
5b2fc70ca1 refactorign buttons view page 2025-06-23 11:16:02 +01:00
Sammii
53af54570c refactoring button variants 2025-06-23 10:57:22 +01:00
Sammii
e0b4626c22 reactor outline button to have hover text map class 2025-06-23 10:04:07 +01:00
Sammii
a273a0db58 quick search style update 2025-06-23 09:39:20 +01:00
Sammii
277104ebba Input style refactor 2025-06-23 09:37:56 +01:00
Sammii
434e59d5c4 refactoring Inputs, improving styling and InputsView page & table props 2025-06-23 08:38:23 +01:00
Sammii
bb20907774 update button props on view table 2025-06-23 08:07:42 +01:00
Sammii
282425575f amend quick search design to ahere to design system 2025-06-23 08:05:52 +01:00
Sammii
75501a9051 amending styling for gcmp homepage 2025-06-20 15:36:55 +01:00
Sammii
c114cf4029 fresh pnpm lock 2025-06-20 11:08:14 +01:00
Sammii
03da7dc994 Merge branch 'main' into feat/design-system-improvements 2025-06-20 11:06:17 +01:00
Sammii
12d5c68f98 update colors view 2025-06-19 14:44:46 +01:00
Sammii
ec1e359621 help links responsive button styling amends 2025-06-19 13:49:15 +01:00
Sammii
44fb13ddd7 refactoring variants with default with black/white baked in on system mode, refactoring Buttons and Icons 2025-06-19 13:48:57 +01:00
Sammii
4fd4c5bbed amending styling on home page 2025-06-19 11:36:01 +01:00
Sammii
a1d31566ed adding views layout to landing page to look like views routes 2025-06-19 11:34:50 +01:00
Sammii
dcde3aa811 amending bg highlight on dark 2025-06-19 11:34:29 +01:00
Sammii
a621141d76 adding colour classes to all colour class maps and improving types 2025-06-18 17:19:07 +01:00
Sammii
0e1fbd7dfc type update 2025-06-18 16:37:31 +01:00
Sammii
27efdf9b8e button instance refactor to new variant colors, code tidy and Layout/view update 2025-06-18 10:59:14 +01:00
Sammii
83068b33e9 refactoring colors into variants 2025-06-18 10:58:23 +01:00
Sammii
769d9b0517 amending HelpLinks, deleting custom styling and refactoring to use button component 2025-06-18 10:36:22 +01:00
Sammii
b0ffe5ed7e creatnig buttong gradient styles with pure tailwind button MVP 2025-06-18 10:35:55 +01:00
Sammii
c67a0cd3ce create and add ThemeProvider component to design system 2025-06-17 17:24:21 +01:00
Sammii
48e11a243f Icon refactor 2025-06-17 17:24:05 +01:00
Sammii
de904698d8 amending Variant import in Icon 2025-06-17 17:01:14 +01:00
Sammii
7c8180dcb4 refactor pages for Layout view with dynamic header based on path 2025-06-17 16:35:01 +01:00
Sammii
da3e101c50 Button StyleVariant Type 2025-06-17 16:17:10 +01:00
Sammii
7605d228f2 page update inputs 2025-06-17 16:10:36 +01:00
Sammii
3063d74ab9 refactoring views, ading new input and icon views to page routes/pages 2025-06-17 16:09:59 +01:00
Sammii
2a4e3fc0cd refactoring components view and icons 2025-06-17 16:04:12 +01:00
Sammii
7cc6d63d40 Icons, styling an code tidy 2025-06-17 15:43:56 +01:00
Sammii
d574bbc521 Icon refactor, has background and refactor icon components on homepage 2025-06-17 15:40:44 +01:00
Sammii
6388a7272b add InputProps table and update ButtonsProps table 2025-06-17 15:23:07 +01:00
Sammii
cd358888d8 Table refactor 2025-06-17 15:22:42 +01:00
Sammii
f427f2324b incorporate icons into design system, with logic to colour button dependant on button variant and style 2025-06-17 15:21:55 +01:00
Sammii
d0e2041b10 styling adjustments 2025-06-13 16:44:16 +01:00
Sammii
edce59d238 making everything fully responsive 2025-06-13 16:35:12 +01:00
Sammii
7f75d852c1 tailwind config amends 2025-06-13 16:21:30 +01:00
Sammii
2683af7d28 ameding tailwind config 2025-06-13 16:14:31 +01:00
Sammii
4223720010 formatting 2025-06-13 16:09:43 +01:00
Sammii
1b4508fea7 making layout with side nav fully responsive 2025-06-13 16:06:21 +01:00
Sammii
776fa09279 refactoring entire design system view for routes base views, created layout and side menu 2025-06-13 15:10:43 +01:00
Sammii
5c200aa60d updating globals.css 2025-06-13 14:13:51 +01:00
Sammii
9fc6c5f6c8 updating Input imports 2025-06-13 14:12:40 +01:00
Sammii
7e06cd4a77 refactor Colors view 2025-06-13 14:12:30 +01:00
Sammii
c43191d97b amending taiwlind config 2025-06-13 14:08:05 +01:00
Sammii
5706b5eb81 Buttons prop table refactor 2025-06-13 14:06:46 +01:00
Sammii
5eed930997 button refactor for outline style variants 2025-06-13 12:35:48 +01:00
Sammii
35e5e50508 table refactor 2025-06-13 12:24:08 +01:00
Sammii
e88a3d0712 adding more variety to Component displays 2025-06-13 10:50:38 +01:00
Sammii
13b64af5b2 adding colour defaulty for dark mode 2025-06-13 10:41:15 +01:00
Sammii
9804c6a729 refactoring Inputs and resolving + showcasing all combinations of labels, positions, icons and buttons available 2025-06-13 10:40:26 +01:00
Sammii
937d415cc2 add home page pnpm lock 2025-06-12 14:59:41 +01:00
Sammii
9196154207 adding states for all button colors 2025-06-12 12:23:48 +01:00
Sammii
c907b2aac2 adding states for all button variants, styleVariants 2025-06-12 12:20:32 +01:00
Sammii
b5a9dfa7ec adding active and focus states to base variant button types 2025-06-12 10:15:23 +01:00
Sammii
113c77b416 removing padding from text buttons 2025-06-12 10:14:37 +01:00
Sammii
cad5444400 more color tweaks 2025-06-12 10:14:04 +01:00
Sammii
b2b350e4d0 adding light dark variables to all semeantic color vars 2025-06-12 10:13:47 +01:00
Sammii
80d5d62852 code tidy 2025-06-12 09:36:08 +01:00
Sammii
75f8833c1a add toaster to design system 2025-06-11 15:14:07 +01:00
Sammii
99d1f3f28b add toast refacvtor table with new isCopyable prop + functionality 2025-06-11 15:13:22 +01:00
Sammii
4066cfe011 adding rest of button props 2025-06-11 13:00:43 +01:00
Sammii
e5f8b06af1 resolve favicon 2025-06-11 13:00:26 +01:00
Sammii
485b5a238d styling Table component 2025-06-11 12:29:40 +01:00
Sammii
c42ee6d6e2 putting the favicon in the public folder 2025-06-11 12:29:23 +01:00
Sammii
1fb2fd0f50 creating button prop table 2025-06-11 12:14:50 +01:00
Sammii
0fce4adfc5 creating Table component 2025-06-11 12:14:37 +01:00
Sammii
b59086c808 doc amend 2025-06-11 11:51:35 +01:00
Sammii
fe91463652 adjusting examples 2025-06-11 11:50:34 +01:00
Sammii
b9065db109 adjusting button styling 2025-06-11 11:45:48 +01:00
Sammii
d13da295ed updating Components 2025-06-11 11:44:26 +01:00
Sammii
0291389c3b update buttons 2025-06-11 11:43:53 +01:00
Sammii
f417f518cb refactor NewsletterForm with new InputWithButton component 2025-06-11 11:43:35 +01:00
Sammii
f69f99a209 create InputWithButton 2025-06-11 11:42:17 +01:00
Sammii
31c4fd7c07 refactor Input 2025-06-11 11:41:35 +01:00
Sammii
613aadd775 create Label 2025-06-11 11:41:10 +01:00
Sammii
0f969b4be4 add search icon to Icon 2025-06-11 11:40:53 +01:00
Sammii
f7ac1015b2 refactor Button to use Icon and remove own ButtonIcon component, introduce icon position 2025-06-11 11:40:02 +01:00
Sammii
d7c5d4a03d styling homepage with design system 2025-06-06 17:49:03 +01:00
Sammii
8384c55cce amending muted tailwind config 2025-06-06 17:48:20 +01:00
Sammii
be947d1086 fixing more paths 2025-06-06 16:19:32 +01:00
Sammii
3924bb2ede making paths fixed 2025-06-06 16:03:15 +01:00
Sammii
2e3003c2fc styling and tweaking Switch and Components 2025-06-06 15:47:32 +01:00
Sammii
5ec15ba0a2 refactoring Forms to Components and adding Switch 2025-06-06 15:44:15 +01:00
Sammii
c7ccf5c5d7 switch refactor 2025-06-06 15:18:33 +01:00
Sammii
9e8c81a1a6 button refactor 2025-06-06 15:18:23 +01:00
Sammii
c563e2547f button refactor 2025-06-06 15:14:54 +01:00
Sammii
00cb697bc7 moving the component views to /src/views 2025-06-06 14:37:06 +01:00
Sammii
a6a0560059 Merge branch 'main' into feat/design-system-improvements 2025-06-06 14:34:38 +01:00
Sammii
55fa5977c2 formatting 2025-06-06 13:48:15 +01:00
Sammii
8b4bc1bc97 updating design system favicon 2025-06-06 13:46:27 +01:00
Sammii
df403ccbc6 styling button new tab icon 2025-06-05 18:41:24 +01:00
Sammii
1f490d2aae reordering style + variant buttons to match style order 2025-06-05 13:38:14 +01:00
Sammii
1b766b6369 UI view reorder 2025-06-05 11:45:33 +01:00
Sammii
3f54da98f8 fix outline button jump 2025-06-05 11:43:47 +01:00
Sammii
05b98c0f8b info update 2025-06-05 11:37:57 +01:00
Sammii
928e63620b renaming styleVariant back 2025-06-05 11:24:16 +01:00
Sammii
ea9b1eb88e renaming symbol styleVariant to style 2025-06-05 10:59:07 +01:00
Sammii
83a88252e2 fighting with tailwind, buttons MVP LGTM 2025-06-05 10:58:40 +01:00
Sammii
7a61c19135 buttons refactor 2025-06-04 17:43:14 +01:00
Sammii
a2f87f304c base structure for component library buttons 2025-06-04 17:00:06 +01:00
Sammii
f9f24d2ad2 styling 2025-06-03 16:48:16 +01:00
Sammii
ed3197a7fd updating schema 2025-06-03 16:47:59 +01:00
Sammii
772a88e98f finessing styling 2025-06-03 16:27:49 +01:00
Sammii
e22a7f46ad stylingg 2025-06-03 15:31:15 +01:00
Sammii
f890f2f460 refactor design system page.tsx to display new components from refactor 2025-06-03 15:16:58 +01:00
Sammii
e9c17e12dc create new Colors view component 2025-06-03 15:15:27 +01:00
Sammii
4b908e3024 refactor Forms view into own component 2025-06-03 15:15:03 +01:00
Sammii
90fd2b5da0 refactor Typography view into own component 2025-06-03 15:14:55 +01:00
Sammii
d7b4360f11 adding error color 2025-06-03 15:13:55 +01:00
113 changed files with 3061 additions and 512 deletions

23
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,23 @@
# Description
<!-- Please include a summary of the change and which issue is fixed -->
<!-- Please also include relevant motivation and context -->
<!-- Include any links to documentation like RFCs if necessary -->
<!-- Add a link to to relevant preview environments or anything that would simplify visual review process -->
<!-- Supplemental screenshots and video are encouraged, but the primary description should be in text -->
## Manual testing instructions
<!-- Add any actions required to manually test the changes -->
## Tests
- [ ] Tests have been added and/or updated
- [ ] Tests have not been updated, because: <!-- Insert reason for not updating tests here -->
- [ ] I need help with writing tests
## Checklist
- [ ] I've updated the part of the docs that are affected the PR changes
- [ ] I've generated a changeset, if a version bump is required
- [ ] I've updated the jsDoc comments to the public APIs I've modified, or added them when missing

View File

@@ -1,5 +1,11 @@
name: Code quality
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
push:
branches:

View File

@@ -1,5 +1,11 @@
name: End-to-End Tests for React Native
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
pull_request:
types: [opened, synchronize, reopened]
@@ -61,7 +67,7 @@ jobs:
disable-animations: true
working-directory: ./examples/chat-rn-expo/
# killall due to this issue: https://github.com/ReactiveCircus/android-emulator-runner/issues/385
script: ./test/e2e/run.sh && killall -INT crashpad_handler || true
script: ./test/e2e/run.sh && ( killall -INT crashpad_handler || true )
- name: Copy Maestro Output
if: steps.e2e_test.outcome != 'success'

View File

@@ -1,5 +1,11 @@
name: Jazz Run Tests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
push:
branches: ["main"]

View File

@@ -1,4 +1,10 @@
name: Playwright Tests
name: Playwright Tests - Homepage
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
push:

View File

@@ -1,5 +1,11 @@
name: Playwright Tests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
push:
branches: ["main"]

View File

@@ -1,4 +1,11 @@
name: Pre-Publish tagged Pull Requests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
pull_request:
types: [opened, synchronize, reopened, labeled]

View File

@@ -1,5 +1,11 @@
name: Unit Tests
concurrency:
# For pushes, this lets concurrent runs happen, so each push gets a result.
# But for other events (e.g. PRs), we can cancel the previous runs.
group: ${{ github.workflow }}-${{ github.event_name == 'push' && github.sha || github.ref }}
cancel-in-progress: true
on:
pull_request:
types: [opened, synchronize, reopened]

View File

@@ -63,7 +63,7 @@ You'll need Node.js 22.x installed (we're working on support for 23.x), and pnpm
4. **Build the packages**:
```bash
pnpm build
pnpm build:packages
```
5. **Run tests** to verify everything is working:

View File

@@ -1,5 +1,34 @@
# passkey-svelte
## 0.0.95
### Patch Changes
- Updated dependencies [a5ceaff]
- jazz-tools@0.15.6
## 0.0.94
### Patch Changes
- Updated dependencies [23bfea5]
- Updated dependencies [e4ba23c]
- Updated dependencies [4b89838]
- jazz-tools@0.15.5
## 0.0.93
### Patch Changes
- jazz-tools@0.15.4
## 0.0.92
### Patch Changes
- Updated dependencies [45f73a7]
- jazz-tools@0.15.3
## 0.0.91
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "chat-svelte",
"version": "0.0.91",
"version": "0.0.95",
"type": "module",
"private": true,
"scripts": {

View File

@@ -58,7 +58,7 @@ export const TodoAccount = co
/** The account migration is run on account creation and on every log-in.
* You can use it to set up the account root and any other initial CoValues you need.
*/
if (!account.root) {
if (account.root === undefined) {
account.root = TodoAccountRoot.create({
projects: co.list(TodoProject).create([], { owner: account }),
});

View File

@@ -17,7 +17,6 @@ import React from "react";
import { TodoAccount, TodoProject } from "./1_schema.ts";
import { NewProjectForm } from "./3_NewProjectForm.tsx";
import { ProjectTodoTable } from "./4_ProjectTodoTable.tsx";
import { apiKey } from "./apiKey";
import {
Button,
ThemeProvider,
@@ -42,7 +41,7 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
return (
<JazzReactProvider
sync={{
peer: `wss://cloud.jazz.tools/?key=${apiKey}`,
peer: `ws://localhost:4200`,
}}
AccountSchema={TodoAccount}
>
@@ -53,6 +52,8 @@ function JazzAndAuth({ children }: { children: React.ReactNode }) {
);
}
// http://localhost:5173/#/project/co_znUD6vciCQazKwAKi4hFwRodxEk
ReactDOM.createRoot(document.getElementById("root")!).render(
<React.StrictMode>
<JazzAndAuth>

View File

@@ -11,7 +11,9 @@ import { useNavigate } from "react-router";
export function NewProjectForm() {
// `me` represents the current user account, which will determine
// access rights to CoValues. We get it from the top-level provider `<WithJazz/>`.
const { me } = useAccount(TodoAccount);
const { me } = useAccount(TodoAccount, {
resolve: { root: { projects: { $each: { $onError: null } } } },
});
const navigate = useNavigate();
const createProject = useCallback(

View File

@@ -5,6 +5,7 @@ export const COLORS = {
GREEN: "#8BDA27",
PINK: "#EF478E",
PURPLE: "#B441EB",
YELLOW: "#FBC400",
YELLOW: "#FCAE00",
RED: "#FF601C",
ORANGE: "#FF601C",
};

View File

@@ -24,6 +24,7 @@
"radix-ui": "^1.4.2",
"react": "catalog:",
"react-dom": "catalog:",
"react-hot-toast": "^2.5.2",
"resend": "^4.0.0",
"tailwind-merge": "^1.14.0",
"tailwindcss": "^3.4.17",

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

Before

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 283 64"><path fill="black" d="M141 16c-11 0-19 7-19 18s9 18 20 18c7 0 13-3 16-7l-7-5c-2 3-6 4-9 4-5 0-9-3-10-7h28v-3c0-11-8-18-19-18zm-9 15c1-4 4-7 9-7s8 3 9 7h-18zm117-15c-11 0-19 7-19 18s9 18 20 18c6 0 12-3 16-7l-8-5c-2 3-5 4-8 4-5 0-9-3-11-7h28l1-3c0-11-8-18-19-18zm-10 15c2-4 5-7 10-7s8 3 9 7h-19zm-39 3c0 6 4 10 10 10 4 0 7-2 9-5l8 5c-3 5-9 8-17 8-11 0-19-7-19-18s8-18 19-18c8 0 14 3 17 8l-8 5c-2-3-5-5-9-5-6 0-10 4-10 10zm83-29v46h-9V5h9zM37 0l37 64H0L37 0zm92 5-27 48L74 5h10l18 30 17-30h10zm59 12v10l-3-1c-6 0-10 4-10 10v15h-9V17h9v9c0-5 6-9 13-9z"/></svg>

Before

Width:  |  Height:  |  Size: 629 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -1,47 +1,46 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
:root {
--color-primary: #146aff;
--color-primary-dark: lch(
from var(--color-primary) calc(l - 15) calc(c - 1) calc(h + 10)
from var(--color-primary) calc(l - 10) calc(c - 1) calc(h + 5)
);
--color-primary-light: lch(
from var(--color-primary) calc(l + 15) calc(c + 1) calc(h - 10)
from var(--color-primary) calc(l + 10) calc(c + 1) calc(h - 5)
);
--color-secondary: var(--color-primary-dark);
--color-highlight: #2dc9c9;
--color-success: #42bb69;
--color-info: #fbc400;
--color-info: #b441eb;
--color-warning: #ff601c;
--color-tip: #b441eb;
--color-success: #8bda27;
--color-alert: #fcae00;
--color-tip: #2dc9c9;
--color-pink: #ef478e;
--color-danger: #ee494c;
--color-default: theme("colors.stone.700");
--color-highlight: theme("colors.stone.900");
--color-strong: theme("colors.stone.800");
--color-muted: theme("colors.stone.300");
--color-transparent-white: rgba(255, 255, 255, 0.1);
--color-transparent-primary: lch(from var(--color-primary-dark) l c h / 0.1);
--color-transparent-primary: lch(from var(--color-primary) l c h / 0.1);
--color-border-default: theme("colors.stone.200");
--color-background-highlight: lch(from var(--color-primary) l c h / 0.25);
}
.dark {
--color-secondary: var(--color-primary-light);
--color-default: theme("colors.stone.400");
--color-highlight: theme("colors.white");
--color-strong: theme("colors.stone.100");
--color-muted: theme("colors.stone.700");
--color-transparent-primary: lch(from var(--color-primary-light) l c h / 0.2);
--color-transparent-primary: lch(from var(--color-primary) l c h / 0.3);
--color-border-default: theme("colors.stone.900");
--color-background-highlight: lch(from var(--color-primary) l c h / 0.5);
}
*:focus {
outline: none;
}
@tailwind base;
@tailwind components;
@tailwind utilities;

View File

@@ -1,5 +1,7 @@
import type { Metadata } from "next";
import "./globals.css";
import { Toaster } from "react-hot-toast";
import { ThemeProvider } from "../components/organisms/ThemeProvider";
import { fontClasses } from "../fonts";
export const metadata: Metadata = {
@@ -13,7 +15,7 @@ export default function RootLayout({
children: React.ReactNode;
}>) {
return (
<html lang="en" className="h-full">
<html lang="en" className="h-full" suppressHydrationWarning>
<body
className={[
...fontClasses,
@@ -21,7 +23,15 @@ export default function RootLayout({
"bg-white dark:bg-stone-950 text-default",
].join(" ")}
>
{children}
<ThemeProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
<Toaster position="bottom-right" />
{children}
</ThemeProvider>
</body>
</html>
);

View File

@@ -1,74 +1,14 @@
import { Prose } from "@components/molecules/Prose";
import { NewsletterForm } from "@components/organisms/NewsletterForm";
import { ViewsLayout } from "./views/ViewsLayout";
import Colors from "./views/colors/page";
export default function Home() {
return (
<main className="container flex flex-col gap-8 py-8 lg:py-16">
<h1 className="text-2xl font-semibold font-display">
Jazz Design System
</h1>
<h2>Typography (Prose)</h2>
<div className="grid gap-4">
<div>
Heading 1
<Prose className="p-3 border">
<h1>Ship top-tier apps at high tempo</h1>
</Prose>
</div>
<div>
Heading 2
<Prose className="p-3 border">
<h2>Ship top-tier apps at high tempo</h2>
</Prose>
</div>
<div>
Heading 3
<Prose className="p-3 border">
<h3>Ship top-tier apps at high tempo</h3>
</Prose>
</div>
<div>
Heading 4
<Prose className="p-3 border">
<h4>Ship top-tier apps at high tempo</h4>
</Prose>
</div>
<div>
Paragraph
<Prose className="p-3 border">
<p>
<strong>Jazz is a framework for building local-first apps</strong>{" "}
an architecture that lets companies like Figma and Linear play
in a league of their own.
</p>
<p>
Open source. Self-host or use Jazz Cloud for zero-config magic.
</p>
</Prose>
</div>
<div>
Link
<Prose className="p-3 border">
This is a <a href="https://jazz.tools">link</a>
</Prose>
</div>
<div>
Code
<Prose className="p-3 border">
This is a one-line <code>piece of code</code>
</Prose>
</div>
</div>
<h2>Newsletter Subscription Form</h2>
<div className="p-3 border">
<NewsletterForm />
<main>
<div className="col-span-8 overflow-y-scroll">
<ViewsLayout>
<h1 className="text-2xl font-bold mt-4">Colors</h1>
<Colors />
</ViewsLayout>
</div>
</main>
);

View File

@@ -0,0 +1,18 @@
"use client";
import { ViewsSideMenu } from "./ViewsSideMenu";
export function ViewsLayout({ children }: { children: React.ReactNode }) {
return (
<div className="container py-8 lg:py-16 relative h-full overflow-hidden flex flex-row gap-2">
<ViewsSideMenu />
<div className="flex-1 overflow-y-scroll overflow-x-hidden pr-8">
<h1 className="text-2xl font-semibold font-display">
Jazz Design System
</h1>
{children}
</div>
</div>
);
}

View File

@@ -0,0 +1,46 @@
"use client";
import clsx from "clsx";
import { useRouter } from "next/navigation";
import { useState } from "react";
import { Button } from "../../components/atoms/Button";
const designSystemTopics = [
"Colors",
"Typography",
"Buttons",
"Components",
"Inputs",
"Icons",
];
export function ViewsSideMenu() {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const router = useRouter();
return (
<div className={clsx("sticky top-0", mobileMenuOpen ? "w-32" : "w-7")}>
<Button
intent="default"
variant="link"
icon={mobileMenuOpen ? "close" : "chevronRight"}
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
size="sm"
/>
{mobileMenuOpen && (
<div className="flex flex-col gap-2">
{designSystemTopics.map((topic) => (
<div key={topic}>
<Button
intent="default"
variant="link"
onClick={() => router.push(`/views/${topic.toLowerCase()}`)}
>
{topic}
</Button>
</div>
))}
</div>
)}
</div>
);
}

View File

@@ -0,0 +1,230 @@
"use client";
import { Button } from "@/components/atoms/Button";
import { Icon } from "@/components/atoms/Icon";
import { Table } from "@/components/molecules/Table";
import {
Dropdown,
DropdownButton,
DropdownItem,
DropdownMenu,
} from "@/components/organisms/Dropdown";
import { useState } from "react";
import { Style } from "../../../utils/tailwindClassesMap";
export default function ButtonsPage() {
const variants = [
"default",
"primary",
"tip",
"info",
"success",
"warning",
"alert",
"danger",
"muted",
"strong",
] as const;
const [selectedVariant, setSelectedVariant] = useState<Style>("default");
return (
<>
<h3 className="text-lg mt-5 mb-2 font-bold">Variants</h3>
<p className="mb-3">
For compatibility the shadcn/ui variants are mapped to the design
system.
</p>
<div className="grid grid-cols-2 gap-2">
<Button variant="default">default</Button>
<Button variant="link">link</Button>
<Button variant="ghost">ghost</Button>
<Button variant="outline">outline</Button>
<Button variant="secondary">secondary</Button>
<Button variant="destructive">destructive</Button>
</div>
<h3 className="text-lg mt-5 mb-2 font-bold">Intents</h3>
<p>
We have extended the shadcn/ui variants to include more styles via the
intent prop.
</p>
<div className="grid grid-cols-2 gap-2">
{/* <Button intent="default">default</Button> */}
<Button intent="primary">primary</Button>
<Button intent="tip">tip</Button>
<Button intent="info">info</Button>
<Button intent="success">success</Button>
<Button intent="warning">warning</Button>
<Button intent="alert">alert</Button>
<Button intent="danger">danger</Button>
<Button intent="muted">muted</Button>
<Button intent="strong">strong</Button>
</div>
<div className="flex justify-between items-center w-48 mt-10">
<h3 className="text-lg font-bold min-w-52">Variants & Intents</h3>
<div className="max-w-xs ml-3">
<Dropdown>
<DropdownButton
className="w-full justify-between"
as={Button}
intent="default"
variant="inverted"
>
{selectedVariant}
<Icon name="chevronDown" size="sm" />
</DropdownButton>
<DropdownMenu>
{variants.map((variant) => (
<DropdownItem
key={variant}
onClick={() => setSelectedVariant(variant)}
>
{variant}
</DropdownItem>
))}
</DropdownMenu>
</Dropdown>
</div>
</div>
<p className="text-sm mt-2 mb-5">
<strong>NB:</strong> Variants and styles are interchangeable. See the
intent on each variant with the dropdown
</p>
<div className="grid grid-cols-2 gap-2">
<Button intent={selectedVariant} variant="outline">
outline
</Button>
<Button intent={selectedVariant} variant="inverted">
inverted
</Button>
<Button intent={selectedVariant} variant="ghost">
ghost
</Button>
<Button intent={selectedVariant} variant="link">
link
</Button>
</div>
<h3 className="text-lg font-bold mt-5">Icons</h3>
<p>Buttons can also contain an icon and text.</p>
<div className="grid grid-cols-2 gap-2">
<Button
icon="delete"
intent="danger"
variant="link"
iconPosition="right"
className="col-span-2 md:col-span-1"
>
text danger with icon
</Button>
<Button
icon="info"
iconPosition="left"
intent="info"
variant="outline"
className="col-span-2 md:col-span-1"
>
outline info with icon
</Button>
<p className="col-span-2">
Or just use the icon prop with any of the button variants, style
variants and colors.
</p>
<Button icon="newsletter" intent="tip" variant="inverted" />
<Button icon="check" intent="success" />
</div>
<div className="overflow-auto">
<h3 className="text-xl mt-5 mb-2 font-bold">Props Table</h3>
<Table tableData={buttonPropsTableData} copyable={true} />
</div>
</>
);
}
const buttonPropsTableData = {
headers: ["prop", "types", "default"],
data: [
{
prop: "intent?",
types: [
"primary",
"tip",
"info",
"success",
"warning",
"alert",
"danger",
"muted",
"strong",
],
default: "default",
},
{
prop: "variant?",
types: [
"default",
"outline",
"inverted",
"ghost",
"link",
"secondary",
"destructive",
],
default: "undefined",
},
{
prop: "icon?",
types: "Lucide icon name",
default: "undefined",
},
{
prop: "iconPosition?",
types: ["left", "right"],
default: "left",
},
{
prop: "loading?",
types: "boolean",
default: "false",
},
{
prop: "loadingText?",
types: "string",
default: "Loading...",
},
{
prop: "disabled?",
types: "boolean",
default: "false",
},
{
prop: "href?",
types: "string",
default: "undefined",
},
{
prop: "newTab?",
types: "boolean",
default: "false",
},
{
prop: "size?",
types: ["sm", "md", "lg"],
default: "md",
},
{
prop: "className?",
types: "string",
default: "undefined",
},
{
prop: "children?",
types: "React.ReactNode",
default: "undefined",
},
],
};

View File

@@ -0,0 +1,85 @@
import clsx from "clsx";
import Link from "next/link";
export default function Colors() {
return (
<>
<p className="p-1">
Jazz uses a color palette which extends tailwind classes, with some
modifications, see{" "}
<Link
href="https://tailwindcss.com/docs/colors#using-color-utilities"
className="text-highlight"
>
Tailwind Color Utilities
</Link>{" "}
for more infomation on basic usage.
</p>
<p className="mt-1 p-1">
Nearly all use cases are encapsulated by harnessing variables which have
a baked in light & dark mode; meaning there are only a limited number of
variables which are required for most development.
<span className="italic">
To see light/dark mode toggle your system settings.
</span>
</p>
<h3 id="color-variables" className="text-md mt-6 mb-1 font-bold">
Color Variables
</h3>
<p className="mb-2 p-1">
The following variables are available and should be used as a preference
to tailwind classes:
</p>
<div className="grid grid-cols-2 gap-2 p-3">
<div className="bg-primary text-white p-3 rounded-md">Primary</div>
<div className="bg-highlight text-white p-3 rounded-md">Highlight</div>
<div className="bg-tip text-white p-3 rounded-md">Tip</div>
<div className="bg-info text-white p-3 rounded-md">Info</div>
<div className="bg-success text-white p-3 rounded-md">Success</div>
<div className="bg-warning text-white p-3 rounded-md">Warning</div>
<div className="bg-alert text-white p-3 rounded-md">Alert</div>
<div className="bg-danger text-white p-3 rounded-md">Danger</div>
<div className="bg-muted text-white p-3 rounded-md">Muted</div>
<div className="bg-strong text-white dark:text-stone-900 p-3 rounded-md">
Strong
</div>
</div>
<p className="text-xs mt-1 mb-4">
NB: These classes should be used across all apps as the primary an
secondary colour are updated per app.
<br />
<br />
For full custimisation, the default colours on tailwind are programmed
to be the tailwind semantic colours, so you can achieve a transparent
primary with `blue/20`.
</p>
<h3 id="text-color-variables" className="text-md mt-6 font-bold">
Text Color Variables
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-2 my-3 px-3">
<ColorTypography />
</div>
</>
);
}
const ColorTypography = () => {
return (
<div className={clsx("text-default rounded-md")}>
<div className="text-default mb-1">text-default</div>
<div className="text-muted mb-1">text-muted</div>
<div className="text-strong mb-1">text-strong</div>
{/* <div>
<span className="text-default bg-highlight">bg-highlight*</span>
</div> */}
<div className="text-strong my-1">
<span className="bg-highlight">text-strong bg-highlight*</span>
</div>
</div>
);
};

View File

@@ -0,0 +1,33 @@
"use client";
import { Switch } from "@/components/atoms/Switch";
import { useState } from "react";
export default function Components() {
const [checked, setChecked] = useState({
md: true,
sm: true,
});
return (
<div className="p-3">
<div className="pb-4 flex gap-6 flex-col md:flex-row">
<h3 className="text-md font-semibold">Switches</h3>
<Switch
label="Switch default (md) (Primary)"
id="switch-md"
checked={checked.md}
onChange={() => setChecked({ ...checked, md: !checked.md })}
/>
<Switch
label="Switch (sm) success"
id="switch-sm"
checked={checked.sm}
onChange={() => setChecked({ ...checked, sm: !checked.sm })}
size="sm"
intent="success"
/>
</div>
</div>
);
}

View File

@@ -0,0 +1,82 @@
import { Icon } from "@/components/atoms/Icon";
import { Table } from "@/components/molecules/Table";
export default function IconsView() {
return (
<div className="p-3">
<div className="flex gap-2">
<Icon name="search" size="xs" intent="primary" />
<Icon name="zip" size="md" intent="info" />
<Icon name="docs" size="lg" intent="success" />
<Icon name="file" size="xl" intent="warning" />
<Icon name="hash" size="2xl" intent="danger" />
<Icon name="help" size="3xl" intent="alert" />
<Icon name="image" size="4xl" intent="tip" />
<Icon name="corecord" size="5xl" intent="default" />
<Icon name="corecord" size="6xl" intent="muted" />
<Icon name="corecord" size="7xl" intent="strong" />
</div>
<div className="flex gap-2">
<Icon name="search" size="xs" intent="primary" hasBackground />
<Icon name="zip" size="md" intent="info" hasBackground />
<Icon name="docs" size="lg" intent="success" hasBackground />
<Icon name="file" size="xl" intent="warning" hasBackground />
<Icon name="hash" size="2xl" intent="danger" hasBackground />
<Icon name="help" size="3xl" intent="alert" hasBackground />
<Icon name="image" size="4xl" intent="tip" hasBackground />
<Icon name="corecord" size="5xl" intent="default" hasBackground />
<Icon name="corecord" size="6xl" intent="muted" hasBackground />
<Icon name="corecord" size="7xl" intent="strong" hasBackground />
</div>
<Table className="mt-6" tableData={iconPropsTable} copyable />
</div>
);
}
const iconPropsTable = {
headers: ["prop", "types", "default"],
data: [
{
prop: "name",
types: "string",
default: "undefined",
},
{
prop: "icon",
types: "LucideIcon",
default: "undefined",
},
{
prop: "size",
types: ["xs", "sm", "md", "lg", "xl", "2xl", "3xl", "4xl", "5xl"],
default: "md",
},
{
prop: "intent",
types: [
"default",
"primary",
"info",
"success",
"warning",
"danger",
"alert",
"tip",
"muted",
"strong",
"white",
],
default: "default",
},
{
prop: "hasBackground",
types: "boolean",
default: "false",
},
{
prop: "className",
types: "string",
default: "undefined",
},
],
};

View File

@@ -0,0 +1,146 @@
"use client";
import { Input } from "@/components/molecules/Input";
import { InputWithButton } from "@/components/molecules/InputWithButton";
import { Table } from "@/components/molecules/Table";
import { NewsletterForm } from "@/components/organisms/NewsletterForm";
import { useState } from "react";
export default function InputsView() {
const [checked, setChecked] = useState({
md: true,
sm: true,
});
return (
<div className="p-3">
<p>
Inputs consist of a combintion of atoms which can be used to create a
variety of inputs. These atoms include:
<br />
<br />
<code>Icon</code>, <code>Label</code> and <code>Button</code>, and also
may be styled with the <code>variant</code> prop.
</p>
<div className="flex flex-col gap-2 mt-3">
<h3 className="text-lg font-semibold my-2">Icons</h3>
<Input
icon="search"
label="Search [label hidden]"
iconPosition="left"
placeholder="Search"
labelHidden={true}
/>
<Input
icon="check"
label="Email"
iconPosition="left"
placeholder="Email"
/>
<Input
icon="file"
label="Password"
iconPosition="right"
placeholder="Password"
/>
<Input
icon="eye"
label="Password"
iconPosition="right"
labelPosition="row"
placeholder="Password"
/>
<h3 className="text-lg font-semibold my-2">Variants</h3>
<Input label="Muted" placeholder="Muted" intent="muted" />
<Input label="Strong" placeholder="Strong" intent="strong" />
<Input label="Default" placeholder="Default" intent="default" />
<h3 className="text-lg font-semibold my-2">Buttons</h3>
<InputWithButton
inputProps={{
label: "Input with button [label visible]",
labelHidden: false,
placeholder: "Input with button",
intent: "success",
}}
buttonProps={{
children: "Let's go",
intent: "success",
variant: "inverted",
icon: "check",
iconPosition: "left",
}}
/>
<InputWithButton
inputProps={{
label: "Input with button [label visible]",
labelHidden: false,
labelPosition: "row",
placeholder: "Input with button",
}}
buttonProps={{
children: "Learn more",
intent: "tip",
variant: "outline",
icon: "corecord",
iconPosition: "right",
}}
/>
<NewsletterForm />
</div>
<Table className="mt-6" tableData={inputPropsTable} copyable />
</div>
);
}
const inputPropsTable = {
headers: ["prop", "types", "default"],
data: [
{
prop: "label",
types: "string",
default: "undefined",
},
{
prop: "labelHidden?",
types: "boolean",
default: "false",
},
{
prop: "labelPosition?",
types: ["column", "row"],
default: "column",
},
{
prop: "icon?",
types: ["LucideIcon"],
default: "undefined",
},
{
prop: "iconPosition?",
types: ["left", "right"],
default: "left",
},
{
prop: "intent?",
types: [
"primary",
"secondary",
"info",
"success",
"warning",
"danger",
"alert",
"tip",
"muted",
"strong",
"default",
],
default: "default",
},
{
prop: "buttonProps?",
types: "see Button Props",
default: "undefined",
},
],
};

View File

@@ -0,0 +1,16 @@
"use client";
import { usePathname } from "next/navigation";
import { ViewsLayout } from "./ViewsLayout";
export default function Layout({ children }: { children: React.ReactNode }) {
const pathname = usePathname();
const viewName = pathname.split("/").pop();
return (
<ViewsLayout>
<h2 className="text-2xl font-bold capitalize my-3">{viewName}</h2>
{children}
</ViewsLayout>
);
}

View File

@@ -0,0 +1,56 @@
import { Prose } from "@/components/molecules/Prose";
export default function Typography() {
return (
<div className="grid gap-4">
<div>
Heading 1
<Prose className="p-3">
<h1>Ship top-tier apps at high tempo</h1>
</Prose>
</div>
<div>
Heading 2
<Prose className="p-3">
<h2>Ship top-tier apps at high tempo</h2>
</Prose>
</div>
<div>
Heading 3
<Prose className="p-3">
<h3>Ship top-tier apps at high tempo</h3>
</Prose>
</div>
<div>
Heading 4
<Prose className="p-3">
<h4>Ship top-tier apps at high tempo</h4>
</Prose>
</div>
<div>
Paragraph
<p className="text-xs text-highlight my-1">
NB: That text can be styled with colour classes, including{" "}
<code>text-muted</code> and <code>text-highlight</code>, see{" "}
<a href="#text-color-variables">Text Color Variables</a>.
</p>
<Prose className="p-3">
<p>
<strong>Jazz is a framework for building local-first apps</strong>
an architecture that lets companies like Figma and Linear play in a
league of their own.
</p>
<p>Open source. Self-host or use Jazz Cloud for zero-config magic.</p>
</Prose>
</div>
<div>
Code
<Prose className="p-3">
This is a one-line <code>piece of code</code>
</Prose>
</div>
</div>
);
}

View File

@@ -1,16 +1,42 @@
import { clsx } from "clsx";
import Link from "next/link";
import { forwardRef } from "react";
import {
Style,
Variant,
VariantColor,
colorToBgActiveMap25,
colorToBgActiveMap50,
colorToBgHoverMap10,
colorToBgHoverMap30,
colorToBgMap,
shadowClassesBase,
sizeClasses,
styleToBgGradientColorMap,
styleToBgGradientHoverMap,
styleToBgTransparentActiveMap,
styleToBorderMap,
styleToButtonStateMap,
styleToColorMap,
styleToHoverShadowMap,
styleToTextActiveMap,
styleToTextHoverMap,
styleToTextMap,
} from "../../utils/tailwindClassesMap";
import { Icon } from "./Icon";
import type { IconName } from "./Icon";
import { Spinner } from "./Spinner";
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
variant?: "primary" | "secondary" | "tertiary" | "destructive" | "plain";
export interface ButtonProps
extends React.ButtonHTMLAttributes<HTMLButtonElement> {
intent?: Style;
variant?: Variant;
state?: "hover" | "active" | "focus" | "disabled";
size?: "sm" | "md" | "lg";
href?: string;
newTab?: boolean;
icon?: IconName;
iconPosition?: "left" | "right" | "center";
loading?: boolean;
loadingText?: string;
children?: React.ReactNode;
@@ -18,63 +44,44 @@ interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
disabled?: boolean;
}
function ButtonIcon({ icon, loading }: ButtonProps) {
if (!Icon) return null;
const className = "size-5";
if (loading) return <Spinner className={className} />;
if (icon) {
return <Icon name={icon} className={className} />;
}
}
export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
(
{
className,
children,
size = "md",
variant = "primary",
intent = "default",
variant,
href,
disabled,
newTab,
loading,
loadingText,
icon,
iconPosition = "left",
type = "button",
...buttonProps
},
ref,
) => {
const sizeClasses = {
sm: "text-sm py-1 px-2",
md: "py-1.5 px-3",
lg: "md:text-lg py-2 px-3 md:px-8 md:py-3",
const styleClass =
styleClasses(intent, variant)[variant as keyof typeof styleClasses] || "";
const getClasses = ({ variant }: { variant: string | undefined }) => {
return {
[sizeClasses[size as keyof typeof sizeClasses]]: size,
[variantClass(intent)]: !variant,
[styleClass]: variant,
};
};
const variantClasses = {
primary:
"bg-primary border border-primary text-white font-medium hover:bg-highlight hover:border-primary hover:text-primary dark:hover:bg-highlight dark:hover:text-primary",
secondary:
"text-stone-900 border font-medium hover:border-primary hover:text-primary hover:bg-highlight hover:dark:border-primary dark:text-white dark:hover:text-primary",
tertiary: "text-primary underline underline-offset-4",
destructive:
"bg-red-600 border-red-600 text-white font-medium hover:bg-red-700 hover:border-red-700",
};
const classNames =
variant === "plain"
? className
: clsx(
className,
"inline-flex items-center justify-center gap-2 rounded-lg text-center transition-colors",
"disabled:pointer-events-none disabled:opacity-70",
sizeClasses[size],
variantClasses[variant],
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
);
const classNames = clsx(
"inline-flex items-center justify-center gap-2 rounded-lg text-center transition-colors w-fit text-nowrap",
getClasses({ variant }),
"disabled:pointer-events-none disabled:opacity-70",
disabled && "opacity-50 cursor-not-allowed pointer-events-none",
className,
);
if (href) {
return (
@@ -83,10 +90,17 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
target={newTab ? "_blank" : undefined}
className={classNames}
>
<ButtonIcon icon={icon} loading={loading} />
{icon && (
<Icon
name={icon}
className={`size-5 ${iconPosition === "left" ? "mr-2" : iconPosition === "right" ? "ml-2" : ""}, ${iconVariant(intent, variant)}`}
/>
)}
{children}
{newTab ? (
<span className="inline-block text-muted relative -top-0.5 -left-2 -mr-2">
<span
className={`inline-block relative -top-0.5 -left-2 -mr-2 ${styleToTextMap[intent as keyof typeof styleToTextMap]}`}
>
</span>
) : (
@@ -104,10 +118,49 @@ export const Button = forwardRef<HTMLButtonElement, ButtonProps>(
className={classNames}
type={type}
>
<ButtonIcon icon={icon} loading={loading} />
{loading ? (
<Spinner className="size-5" />
) : (
icon &&
iconPosition === "left" && (
<Icon name={icon} intent={iconVariant(intent, variant)} />
)
)}
{loading && loadingText ? loadingText : children}
{icon && iconPosition === "right" && (
<Icon
name={icon}
intent={iconVariant(intent, variant)}
hasHover={true}
/>
)}
</button>
);
},
);
const iconVariant = (intent: Style, variant: Variant | undefined) => {
return variant ? intent : intent === "default" ? "default" : "white";
};
const textColorVariant = (style: Style) => {
return style === "default"
? "text-stone-700 dark:text-white hover:text-stone-800 active:text-stone-700 dark:hover:text-stone-100 dark:active:text-stone-200"
: style === "strong"
? "text-stone-100 dark:text-stone-900"
: "text-white";
};
const variantClass = (intent: Style) =>
`${styleToBgGradientColorMap[intent]} ${styleToBgGradientHoverMap[intent]} ${textColorVariant(intent)} ${styleToButtonStateMap[intent]} ${shadowClassesBase} shadow-stone-400/20`;
const styleClasses = (intent: Style, variant: Variant | undefined) => {
return {
outline: `border ${styleToBorderMap[intent]} ${styleToTextMap[intent]} ${styleToTextHoverMap[intent]} ${styleToHoverShadowMap[intent]} ${styleToBgTransparentActiveMap[intent]} shadow-[5px_0px]`,
inverted: `${styleToTextMap[intent]} ${colorToBgHoverMap30[styleToColorMap[intent] as VariantColor]} ${colorToBgMap[styleToColorMap[intent] as VariantColor]} ${colorToBgActiveMap50[styleToColorMap[intent] as VariantColor]} ${shadowClassesBase}`,
ghost: `bg-transparent ${styleToTextMap[intent]} ${colorToBgHoverMap10[styleToColorMap[intent] as VariantColor]} ${colorToBgActiveMap25[styleToColorMap[intent] as VariantColor]}`,
link: `bg-transparent ${styleToTextMap[intent]} underline underline-offset-2 p-0 hover:bg-transparent ${styleToTextHoverMap[intent]} ${styleToTextActiveMap[intent]} active:underline-stone-500`,
secondary: `bg-stone-300 ${styleToTextMap[intent]} hover:bg-stone-400/80 active:bg-stone-500/80`,
destructive: `bg-danger text-white hover:bg-red/80 active:bg-red/70`,
default: `${styleToBgGradientColorMap["default"]} ${styleToBgGradientHoverMap["default"]} ${textColorVariant("default")} ${styleToButtonStateMap["default"]} ${shadowClassesBase} shadow-stone-400/20`,
};
};

View File

@@ -14,6 +14,7 @@ import {
ChevronRightIcon,
ClipboardIcon,
CodeIcon,
Eye,
FileLock2Icon,
FileTextIcon,
FingerprintIcon,
@@ -46,9 +47,15 @@ import {
XIcon,
} from "lucide-react";
import clsx from "clsx";
import {
Style,
styleToTextHoverMap,
styleToTextMap,
} from "../../utils/tailwindClassesMap";
import { GcmpIcons } from "./icons";
const icons = {
export const icons = {
arrowDown: ArrowDownIcon,
arrowRight: ArrowRightIcon,
auth: UserIcon,
@@ -100,6 +107,7 @@ const icons = {
// text editor icons
bold: BoldIcon,
italic: ItalicIcon,
eye: Eye,
};
// copied from tailwind line height https://tailwindcss.com/docs/font-size
@@ -107,8 +115,8 @@ const sizes = {
"2xs": 14,
xs: 16,
sm: 20,
md: 24,
lg: 28,
md: 22,
lg: 26,
xl: 28,
"2xl": 32,
"3xl": 36,
@@ -143,13 +151,19 @@ export function Icon({
name,
icon,
size = "md",
intent = "default",
hasBackground = false,
className,
hasHover = false,
...svgProps
}: {
name?: IconName;
icon?: LucideIcon;
size?: keyof typeof sizes;
intent?: Style | "white";
hasBackground?: boolean;
className?: string;
hasHover?: boolean;
} & React.SVGProps<SVGSVGElement>) {
if (!icon && (!name || !icons.hasOwnProperty(name))) {
throw new Error(`Icon not found: ${name}`);
@@ -158,13 +172,52 @@ export function Icon({
// @ts-ignore
const IconComponent = icons?.hasOwnProperty(name) ? icons[name] : icon;
const iconClass = {
...styleToTextMap,
white: "text-white",
};
const iconHoverClass = {
...styleToTextHoverMap,
white: "hover:text-white/90",
};
const backgroundClasses = {
default: "bg-stone-200/30 dark:bg-stone-900/30",
primary: "bg-primary-transparent",
secondary: "bg-secondary-transparent",
info: "bg-info-transparent",
success: "bg-success-transparent",
warning: "bg-warning-transparent",
danger: "bg-danger-transparent",
alert: "bg-alert-transparent",
tip: "bg-tip-transparent",
muted: "bg-stone-300/30 dark:bg-stone-700/30",
strong: "bg-stone-900/30 dark:bg-stone-100/30",
};
const roundedClasses = {
xs: "rounded-xs",
sm: "rounded-sm",
md: "rounded-md",
lg: "rounded-lg",
xl: "rounded-xl",
};
return (
<IconComponent
aria-hidden="true"
size={sizes[size]}
strokeWidth={strokeWidths[size]}
strokeLinecap="round"
className={className}
className={clsx(
roundedClasses[size as keyof typeof roundedClasses] || "rounded-lg",
iconClass[intent as keyof typeof iconClass],
hasBackground &&
backgroundClasses[intent as keyof typeof backgroundClasses],
hasHover && iconHoverClass[intent as keyof typeof iconHoverClass],
className,
)}
{...svgProps}
/>
);

View File

@@ -0,0 +1,13 @@
import { Label as LabelRadix } from "radix-ui";
export function Label({
label,
htmlFor,
className,
}: { label: string; htmlFor: string; className?: string }) {
return (
<LabelRadix.Root className={className} htmlFor={htmlFor}>
{label}
</LabelRadix.Root>
);
}

View File

@@ -14,7 +14,7 @@ export function Spinner({ className }: { className?: string }) {
cy="12"
r="10"
stroke="currentColor"
stroke-width="4"
strokeWidth="4"
></circle>
<path
className="opacity-75"

View File

@@ -1,23 +1,26 @@
import clsx from "clsx";
import { Switch as RadixSwitch } from "radix-ui";
import { Style, styleToBgMap } from "../../utils/tailwindClassesMap";
export function Switch({
id,
size = "sm",
size = "md",
intent = "primary",
checked,
onChange,
label,
}: {
id: string;
size?: "sm" | "md";
intent?: Style;
checked: boolean;
onChange: (checked: boolean) => void;
label?: string;
}) {
return (
<div className="flex items-center gap-2 w-content">
<div className="flex items-center w-content">
<label
className={clsx("text-xs text-gray-500", labelSizeClass[size])}
className={clsx("text-gray-500 mr-2", labelSizeClass[size])}
htmlFor={id}
>
{label}
@@ -25,18 +28,21 @@ export function Switch({
<RadixSwitch.Root
id={id}
className={clsx(
"min-w-10 h-6 rounded-full relative",
size === "sm" && "min-w-6 h-3.5",
checked ? "bg-primary" : "bg-stone-200",
"rounded-full relative",
size === "sm" ? "min-w-6 h-4" : "min-w-10 h-6",
checked ? styleToBgMap[intent] : "bg-stone-200",
)}
checked={checked}
onCheckedChange={onChange}
>
<RadixSwitch.Thumb
className={clsx(
"block w-4 h-4 bg-white rounded-full transition-transform duration-300 translate-x-0 ml-[0.06rem]",
size === "sm" && "w-3 h-3",
checked && "translate-x-[0.6rem]",
"block bg-white rounded-full transition-transform duration-300 translate-x-0 ml-[0.1em]",
size === "sm" ? "w-3 h-3" : "w-5 h-5",
checked &&
(size === "sm"
? "translate-x-[0.5rem]"
: "translate-x-[1.01rem]"),
)}
/>
</RadixSwitch.Root>

View File

@@ -21,7 +21,9 @@ export function FeatureCard({
{icon && (
<Icon
name={icon}
className="text-primary p-1.5 rounded-lg bg-blue-50 dark:bg-stone-900 mb-2.5"
intent="primary"
hasBackground
className="p-1.5 rounded-lg mb-2.5"
size="3xl"
/>
)}

View File

@@ -1,16 +1,47 @@
import { clsx } from "clsx";
import { forwardRef, useId } from "react";
import { Style, styleToActiveBorderMap } from "../../utils/tailwindClassesMap";
import { Button, ButtonProps } from "../atoms/Button";
import { Icon, icons } from "../atoms/Icon";
import { Label } from "../atoms/Label";
interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
// label can be hidden with a "label:sr-only" className
export interface InputProps
extends React.InputHTMLAttributes<HTMLInputElement> {
label: string;
className?: string;
id?: string;
placeholder?: string;
icon?: keyof typeof icons;
iconPosition?: "left" | "right";
labelHidden?: boolean;
labelPosition?: "column" | "row";
button?: ButtonProps;
intent?: Style;
}
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ label, className, id: customId, ...inputProps }, ref) => {
(
{
label,
className,
id: customId,
placeholder,
icon,
iconPosition = "left",
labelHidden,
labelPosition,
button,
intent = "default",
...inputProps
},
ref,
) => {
const generatedId = useId();
const id = customId || generatedId;
const inputIconClassName =
icon && iconPosition === "left"
? "pl-9"
: icon && iconPosition === "right";
const inputClassName = clsx(
"w-full rounded-md border px-3.5 py-2 shadow-sm",
@@ -18,15 +49,53 @@ export const Input = forwardRef<HTMLInputElement, InputProps>(
"dark:text-white dark:bg-stone-925",
);
const containerClassName = clsx("grid gap-1", className);
return (
<div className={containerClassName}>
<label htmlFor={id} className="text-stone-600 dark:text-stone-300">
{label}
</label>
<input ref={ref} {...inputProps} id={id} className={inputClassName} />
<div
className={clsx(
"relative w-full",
labelPosition === "row" ? "flex flex-row items-center" : "",
)}
>
<Label
label={label}
htmlFor={id}
className={clsx(
labelPosition === "row" ? "mr-2" : "w-full",
labelHidden ? "sr-only" : "",
)}
/>
<div className={clsx("flex gap-2 w-full items-center")}>
<input
ref={ref}
{...inputProps}
id={id}
className={clsx(
inputClassName,
inputIconClassName,
className,
"px-2",
styleToActiveBorderMap[
intent as keyof typeof styleToActiveBorderMap
],
)}
placeholder={placeholder}
/>
{icon && (
<Icon
name={icon}
className={clsx(
"absolute",
iconPosition === "left"
? "left-2"
: iconPosition === "right"
? "right-2"
: "",
)}
intent={intent}
/>
)}
{button && <Button {...button} />}
</div>
</div>
);
},

View File

@@ -0,0 +1,16 @@
import { ButtonProps } from "../atoms/Button";
import { Input, InputProps } from "./Input";
export function InputWithButton({
inputProps,
buttonProps,
}: {
inputProps: InputProps;
buttonProps: ButtonProps;
}) {
return (
<div className="flex gap-2 w-full">
<Input {...inputProps} button={buttonProps} />
</div>
);
}

View File

@@ -0,0 +1,110 @@
"use client";
import { clsx } from "clsx";
import { toast } from "react-hot-toast";
export interface TableProps extends React.HTMLAttributes<HTMLTableElement> {
className?: string;
tableData: {
headers: string[];
data: {
[key: string]: string | string[];
}[];
};
copyable?: boolean;
}
export function Table({
className,
tableData,
copyable,
...tableProps
}: TableProps) {
return (
<table
className={clsx(
"w-full border border-gray-200 rounded-lg overflow-hidden overflow-x-scroll",
className,
)}
{...tableProps}
>
<thead>
<tr>
{tableData.headers.map((header) => (
<th key={header} className="text-left pl-1 capitalize">
{header}
</th>
))}
</tr>
</thead>
<tbody className="border-t border-gray-200">
{tableData.data.map((row, index) => (
<tr
key={`${row.id as string}-${index}=${tableData.headers.join("-")}`}
className={clsx(
index % 2 === 0
? "bg-stone-200/20 dark:bg-stone-800/40 hover:bg-stone-200/70 dark:hover:bg-stone-800/90"
: "hover:bg-stone-200/50 dark:hover:bg-stone-100/20",
"border-b border-stone-200 text-stone-800 hover:text-black dark:text-stone-200 dark:hover:text-white",
)}
>
{tableData.headers.map((header, index) => (
<td
key={header}
className={clsx(
index === 0 && "pl-1",
typeof row[header] !== "string" && "flex",
)}
>
{typeof row[header] !== "string" ? (
<TableDataContainer>
{row[header]?.map((item) => (
<div
className={clsx(
"hover:underline",
copyable && "cursor-pointer",
)}
key={item}
onClick={() => {
if (copyable) {
navigator.clipboard.writeText(item.toString());
toast.success("Copied to clipboard");
}
}}
>
{item}
</div>
))}
</TableDataContainer>
) : (
<TableDataContainer isCopyable={copyable}>
{row[header as keyof typeof row]}
</TableDataContainer>
)}
</td>
))}
</tr>
))}
</tbody>
</table>
);
}
const TableDataContainer = ({
children,
className,
isCopyable,
}: { children: React.ReactNode; className?: string; isCopyable?: boolean }) => {
return (
<div
className={clsx("flex gap-2", className, isCopyable && "cursor-pointer")}
onClick={() => {
if (isCopyable && children) {
navigator.clipboard.writeText(children.toString());
toast.success("Copied to clipboard");
}
}}
>
{children}
</div>
);
};

View File

@@ -170,6 +170,7 @@ export function MobileNav({
{item.title}
</NavLink>
))}
{cta}
</div>
</>
),
@@ -200,7 +201,6 @@ export function MobileNav({
<NavLinkLogo prominent href="/" className="mr-auto">
{mainLogo}
</NavLinkLogo>
{cta}
<button
className="flex gap-2 p-3 rounded-xl items-center"
onClick={() => {

View File

@@ -3,13 +3,11 @@
import { useState } from "react";
import { ErrorResponse } from "resend";
import { subscribe } from "../../actions/resend";
import { Button } from "../atoms/Button";
import { Icon } from "../atoms/Icon";
import { Input } from "../molecules/Input";
import { InputWithButton } from "../molecules/InputWithButton";
export function NewsletterForm() {
const [email, setEmail] = useState("");
// const [subscribed, setSubscribed] = useState(false);
const [error, setError] = useState<ErrorResponse | undefined>();
const [state, setState] = useState<"ready" | "loading" | "success" | "error">(
@@ -41,32 +39,37 @@ export function NewsletterForm() {
}
if (state === "error" && error?.message) {
return <p className="text-red-700">Error: {error.message}</p>;
return <p className="text-danger">Error: {error.message}</p>;
}
return (
<form action="" onSubmit={submit} className="flex gap-x-4 w-120 max-w-md">
<Input
id="email-address"
name="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
placeholder="Enter your email"
autoComplete="email"
className="flex-1 label:sr-only"
label="Email address"
<InputWithButton
inputProps={{
id: "email-address",
name: "email",
type: "email",
value: email,
onChange: (e) => setEmail(e.target.value),
required: true,
placeholder: "Enter your email",
autoComplete: "email",
className: "flex-1",
label: "Email address",
labelHidden: true,
intent: "primary",
}}
buttonProps={{
type: "submit",
intent: "primary",
variant: "outline",
loadingText: "Subscribing...",
loading: state === "loading",
icon: "newsletter",
iconPosition: "right",
children: "Subscribe",
}}
/>
<Button
type="submit"
variant="secondary"
loadingText="Subscribing..."
loading={state === "loading"}
icon="newsletter"
>
Subscribe
</Button>
</form>
);
}

View File

@@ -0,0 +1,39 @@
"use client";
import { ThemeProvider as NextThemesProvider, useTheme } from "next-themes";
import { type ThemeProviderProps } from "next-themes/dist/types";
import * as React from "react";
import { useEffect } from "react";
function ThemeWatcher() {
let { resolvedTheme, setTheme } = useTheme();
useEffect(() => {
let media = window.matchMedia("(prefers-color-scheme: dark)");
function onMediaChange() {
let systemTheme = media.matches ? "dark" : "light";
if (resolvedTheme === systemTheme) {
setTheme("system");
}
}
onMediaChange();
media.addEventListener("change", onMediaChange);
return () => {
media.removeEventListener("change", onMediaChange);
};
}, [resolvedTheme, setTheme]);
return null;
}
export function ThemeProvider({ children, ...props }: ThemeProviderProps) {
return (
<NextThemesProvider {...props}>
<ThemeWatcher />
{children}
</NextThemesProvider>
);
}

View File

@@ -0,0 +1,13 @@
import { co, z } from "jazz-tools";
// Example CoMap class
export const Person = co.map({
name: z.string(),
age: z.number(),
height: z.number().optional(),
weight: z.number().optional(),
});
export const ListOfPeople = co.list(Person);
export const PersonFeed = co.feed(Person);

View File

@@ -0,0 +1,308 @@
export type Variant =
| "default"
| "secondary"
| "destructive"
| "ghost"
| "outline"
| "link"
| "inverted";
export type Style =
| "default"
| "primary"
| "tip"
| "info"
| "success"
| "warning"
| "alert"
| "danger"
| "muted"
| "strong";
export const sizeClasses = {
sm: "text-sm py-1 px-2",
md: "py-1.5 px-3",
lg: "md:text-lg py-2 px-3 md:px-8 md:py-3",
};
export const styleToBorderMap = {
primary: "border-primary",
info: "border-info",
success: "border-success",
warning: "border-warning",
danger: "border-danger",
alert: "border-alert",
tip: "border-tip",
muted: "border-stone-200 dark:border-stone-700",
strong: "border-stone-900 dark:border-stone-100",
default: "border-stone-600 dark:border-stone-200",
};
export const styleToActiveBorderMap = {
primary: "active:border-primary-transparent focus:border-primary-transparent",
info: "active:border-info-transparent focus:border-info-transparent",
success: "active:border-success-transparent focus:border-success-transparent",
warning: "active:border-warning-transparent focus:border-warning-transparent",
danger: "active:border-danger-transparent focus:border-danger-transparent",
alert: "active:border-alert-transparent focus:border-alert-transparent",
tip: "active:border-tip-transparent focus:border-tip-transparent",
muted:
"active:border-stone-200/30 focus:border-stone-200/30 dark:active:border-stone-900/30 dark:focus:border-stone-900/30",
strong:
"active:border-stone-900/30 focus:border-stone-900/30 dark:active:border-stone-200/30 dark:focus:border-stone-200/30",
default:
"active:border-stone-600/30 dark:active:border-stone-100/30 focus:border-stone-600/30 dark:focus:border-stone-100/30",
};
export const styleToBgMap = {
primary: "bg-primary",
info: "bg-info",
success: "bg-success",
warning: "bg-warning",
danger: "bg-danger",
alert: "bg-alert",
tip: "bg-tip",
muted: "bg-stone-200 dark:bg-stone-900",
strong: "bg-stone-900 dark:bg-stone-200",
default: "bg-stone-700 dark:bg-stone-100",
};
export const styleToBgTransparentHoverMap = {
primary: "hover:bg-primary-transparent",
info: "hover:bg-info-transparent",
success: "hover:bg-success-transparent",
warning: "hover:bg-warning-transparent",
danger: "hover:bg-danger-transparent",
alert: "hover:bg-alert-transparent",
tip: "hover:bg-tip-transparent",
muted: "hover:bg-stone-100/20 dark:hover:bg-stone-900/20",
strong: "hover:bg-stone-900/20 dark:hover:bg-stone-100/20",
default: "hover:bg-stone-600/20 dark:hover:bg-stone-100/20",
};
export const styleToBgTransparentActiveMap = {
primary: "active:bg-blue/20",
info: "active:bg-purple/20",
success: "active:bg-green/20",
warning: "active:bg-orange/20",
danger: "active:bg-red/20",
alert: "active:bg-yellow/20",
tip: "active:bg-cyan/20",
muted: "active:bg-stone-400/20",
strong: "active:bg-stone-900/20",
default: "active:bg-stone-600/20 dark:active:bg-stone-100/20",
};
export const styleToTextMap = {
primary: "text-primary",
info: "text-info",
success: "text-success",
warning: "text-warning",
danger: "text-danger",
alert: "text-alert",
tip: "text-tip",
muted: "text-stone-500 dark:text-stone-400",
strong: "text-stone-900 dark:text-white",
default: "text-stone-700 dark:text-stone-100",
};
export const styleToTextHoverMap = {
primary: "hover:text-primary-light",
info: "hover:text-info-light",
success: "hover:text-success-light",
warning: "hover:text-warning-light",
danger: "hover:text-danger-light",
alert: "hover:text-alert-light",
tip: "hover:text-tip-light",
muted: "hover:text-stone-400 dark:hover:text-stone-500",
strong: "hover:text-stone-700 dark:hover:text-stone-300",
default: "hover:text-stone-600 dark:hover:text-stone-200",
};
export const styleToTextActiveMap = {
primary: "active:text-primary-dark",
info: "active:text-info-dark",
success: "active:text-success-dark",
warning: "active:text-warning-dark",
danger: "active:text-danger-dark",
alert: "active:text-alert-dark",
tip: "active:text-tip-dark",
muted: "active:text-stone-400 dark:active:text-stone-500",
strong: "active:text-stone-700 dark:active:text-stone-300",
default: "active:text-stone-800 dark:active:text-stone-400",
};
export type VariantColor =
| "blue"
| "indigo"
| "purple"
| "green"
| "orange"
| "red"
| "yellow"
| "cyan"
| "muted"
| "strong"
| "default";
export const styleToColorMap = {
primary: "blue",
info: "purple",
success: "green",
warning: "orange",
danger: "red",
alert: "yellow",
tip: "cyan",
muted: "muted",
strong: "strong",
default: "default",
};
export const colorToBgMap = {
blue: "bg-blue/20",
indigo: "bg-indigo-500/20",
purple: "bg-purple/20",
green: "bg-green/20",
orange: "bg-orange/20",
red: "bg-red/20",
yellow: "bg-yellow/20",
cyan: "bg-cyan/20",
muted: "bg-stone-200/20 dark:bg-stone-900/50",
strong: "bg-stone-900/20 dark:bg-stone-100/50",
default: "bg-stone-600/20 dark:bg-white/20",
};
export const colorToBgHoverMap30 = {
blue: "hover:bg-blue/30",
indigo: "hover:bg-indigo-500/30",
purple: "hover:bg-purple/30",
green: "hover:bg-green/30",
orange: "hover:bg-orange/30",
red: "hover:bg-red/30",
yellow: "hover:bg-yellow/30",
cyan: "hover:bg-cyan/30",
muted: "hover:bg-stone-200/30 dark:hover:bg-stone-900/30",
strong: "hover:bg-stone-900/30 dark:hover:bg-stone-100/30",
default: "hover:bg-stone-600/30 dark:hover:bg-white/30",
};
export const colorToBgHoverMap10 = {
blue: "hover:bg-blue/10",
indigo: "hover:bg-indigo-500/10",
purple: "hover:bg-purple/10",
green: "hover:bg-green/10",
orange: "hover:bg-orange/10",
red: "hover:bg-red/10",
yellow: "hover:bg-yellow/10",
cyan: "hover:bg-cyan/10",
muted: "hover:bg-stone-200/30 dark:hover:bg-stone-800/30",
strong: "hover:bg-stone-900/10 dark:hover:bg-stone-100/10",
default: "hover:bg-stone-600/10 dark:hover:bg-white/10",
};
export const colorToBgActiveMap50 = {
blue: "active:bg-blue/50",
indigo: "active:bg-indigo-500/50",
purple: "active:bg-purple/50",
green: "active:bg-green/50",
orange: "active:bg-orange/50",
red: "active:bg-red/50",
yellow: "active:bg-yellow/50",
cyan: "active:bg-cyan/50",
muted: "active:bg-stone-100/50 dark:active:bg-stone-900/50",
strong: "active:bg-stone-800/40 dark:active:bg-stone-200/40",
default: "active:bg-stone-900/40 dark:active:bg-white/50",
};
export const colorToBgActiveMap25 = {
blue: "active:bg-blue/25",
indigo: "active:bg-indigo-500/25",
purple: "active:bg-purple/25",
green: "active:bg-green/25",
orange: "active:bg-orange/25",
red: "active:bg-red/25",
yellow: "active:bg-yellow/25",
cyan: "active:bg-cyan/25",
muted: "active:bg-stone-100/25 dark:active:bg-stone-900/25",
strong: "active:bg-stone-900/25 dark:active:bg-stone-100/25",
default: "active:bg-black/25 dark:active:bg-white/25",
};
const gradiantClassesBase = "bg-gradient-to-t from-7% via-50% to-95%";
export const styleToBgGradientColorMap = {
primary: `from-primary-dark via-primary to-primary-light ${gradiantClassesBase}`,
info: `from-info-dark via-info to-info-light ${gradiantClassesBase}`,
success: `from-success-dark via-success to-success-light ${gradiantClassesBase}`,
warning: `from-warning-dark via-warning to-warning-light ${gradiantClassesBase}`,
danger: `from-danger-dark via-danger to-danger-light ${gradiantClassesBase}`,
alert: `from-alert-dark via-alert to-alert-light ${gradiantClassesBase}`,
tip: `from-tip-dark via-tip to-tip-light ${gradiantClassesBase}`,
muted: `from-stone-200 via-stone-300 to-stone-400 ${gradiantClassesBase} dark:from-stone-900 dark:via-stone-900 dark:to-stone-800`,
strong: `from-stone-700 via-stone-800 to-stone-900 ${gradiantClassesBase} dark:from-stone-100 dark:via-stone-200 dark:to-stone-300`,
default: `from-stone-200/40 via-white to-stone-100 ${gradiantClassesBase} dark:from-stone-900 dark:via-black dark:to-stone-950`,
};
export const styleToBgGradientHoverMap = {
primary: `hover:from-primary-brightLight hover:to-primary-light ${gradiantClassesBase}`,
info: `hover:from-info-brightLight hover:to-info-light ${gradiantClassesBase}`,
success: `hover:from-success-brightLight hover:to-success-light ${gradiantClassesBase}`,
warning: `hover:from-warning-brightLight hover:to-warning-light ${gradiantClassesBase}`,
danger: `hover:from-danger-brightLight hover:to-danger-light ${gradiantClassesBase}`,
alert: `hover:from-alert-brightLight hover:to-alert-light ${gradiantClassesBase}`,
tip: `hover:from-tip-brightLight hover:to-tip-light ${gradiantClassesBase}`,
muted: `hover:from-stone-200 hover:to-stone-300 ${gradiantClassesBase} dark:hover:from-stone-900 dark:hover:to-stone-700/70`,
strong: `hover:from-stone-700 hover:to-stone-800 ${gradiantClassesBase} dark:hover:from-stone-100 dark:hover:to-stone-200`,
default: `hover:from-stone-100/50 hover:to-stone-100/50 dark:hover:from-stone-950 dark:hover:to-stone-900 ${gradiantClassesBase} border border-stone-100 dark:border-stone-900`,
};
export const styleToBgGradientActiveMap = {
primary: `active:from-primary-brightDark active:to-primary-light ${gradiantClassesBase}`,
info: `active:from-info-brightDark active:to-info-light ${gradiantClassesBase}`,
success: `active:from-success-brightDark active:to-success-light ${gradiantClassesBase}`,
warning: `active:from-warning-brightDark active:to-warning-light ${gradiantClassesBase}`,
danger: `active:from-danger-brightDark active:to-danger-light ${gradiantClassesBase}`,
alert: `active:from-alert-brightDark active:to-alert-light ${gradiantClassesBase}`,
tip: `active:from-tip-brightDark active:to-tip-light ${gradiantClassesBase}`,
muted: `active:from-stone-300 active:to-stone-300 ${gradiantClassesBase} dark:active:from-stone-900 dark:active:to-stone-800`,
strong: `active:from-stone-950 active:to-stone-900 ${gradiantClassesBase} dark:active:from-stone-100 dark:active:to-stone-200`,
default: `active:from-stone-200/50 active:to-stone-100/50 dark:active:from-stone-950 dark:active:to-black ${gradiantClassesBase}`,
};
export const shadowClassesBase = "shadow-sm";
export const styleToHoverShadowMap = {
primary: `${shadowClassesBase} shadow-blue/20 hover:shadow-blue/40`,
info: `${shadowClassesBase} shadow-purple/20 hover:shadow-purple/30`,
success: `${shadowClassesBase} shadow-green/20 hover:shadow-green/30`,
warning: `${shadowClassesBase} shadow-orange/20 hover:shadow-orange/30`,
danger: `${shadowClassesBase} shadow-red/20 hover:shadow-red/30`,
alert: `${shadowClassesBase} shadow-yellow/20 hover:shadow-yellow/30`,
tip: `${shadowClassesBase} shadow-cyan/20 hover:shadow-cyan/30`,
muted: `${shadowClassesBase} shadow-stone-200/20 hover:shadow-stone-200/30 dark:shadow-stone-600/20 dark:hover:shadow-stone-600/30`,
strong: `${shadowClassesBase} shadow-stone-900/20 hover:shadow-stone-900/30 dark:shadow-white/20 dark:hover:shadow-white/30`,
default: `${shadowClassesBase} shadow-stone-600/20 hover:shadow-stone-600/30 dark:shadow-stone-200/20 dark:hover:shadow-stone-200/30`,
};
const focusRingClassesBase =
"focus:outline-none focus-visible:ring focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:ring-offset-opacity-10";
export const styleToButtonStateMap = {
primary: `${styleToBgGradientActiveMap.primary} ${focusRingClassesBase} focus:ring-primary`,
info: `${styleToBgGradientActiveMap.info} ${focusRingClassesBase} focus:ring-info`,
success: `${styleToBgGradientActiveMap.success} ${focusRingClassesBase} focus:ring-success`,
warning: `${styleToBgGradientActiveMap.warning} ${focusRingClassesBase} focus:ring-warning`,
danger: `${styleToBgGradientActiveMap.danger} ${focusRingClassesBase} focus:ring-danger`,
alert: `${styleToBgGradientActiveMap.alert} ${focusRingClassesBase} focus:ring-alert`,
tip: `${styleToBgGradientActiveMap.tip} ${focusRingClassesBase} focus:ring-tip`,
muted: `${styleToBgGradientActiveMap.muted} ${focusRingClassesBase} focus:ring-stone-200 dark:focus:ring-stone-900`,
strong: `${styleToBgGradientActiveMap.strong} ${focusRingClassesBase} focus:ring-stone-800 dark:focus:ring-stone-200`,
default: `${styleToBgGradientActiveMap.default} ${focusRingClassesBase} focus:ring-black dark:focus:ring-white`,
};
export const variantStyleToButtonStateMap = {
outline: `${focusRingClassesBase}`,
inverted: `${focusRingClassesBase}`,
ghost: `${focusRingClassesBase}`,
text: `${focusRingClassesBase}`,
};

View File

@@ -30,6 +30,36 @@ const jazzBlue = {
DEFAULT: COLORS.BLUE,
};
const green = {
...colors.green,
DEFAULT: COLORS.FOREST,
};
const cyan = {
...colors.cyan,
DEFAULT: COLORS.TURQUOISE,
};
const red = {
...colors.red,
DEFAULT: COLORS.RED,
};
const yellow = {
...colors.yellow,
DEFAULT: COLORS.YELLOW,
};
const orange = {
...colors.orange,
DEFAULT: COLORS.ORANGE,
};
const purple = {
...colors.purple,
DEFAULT: COLORS.PURPLE,
};
const stonePaletteWithAlpha = { ...stonePalette };
Object.keys(stonePalette).forEach((key) => {
@@ -40,11 +70,12 @@ Object.keys(stonePalette).forEach((key) => {
});
/** @type {import('tailwindcss').Config} */
const config = {
export const preset = {
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
"./src/utils/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
@@ -52,28 +83,103 @@ const config = {
...harmonyPalette,
stone: stonePaletteWithAlpha,
blue: jazzBlue,
primary: "var(--color-primary)",
secondary: "var(--color-secondary)",
highlight: "var(--color-transparent-primary)",
success: "var(--color-success)",
info: "var(--color-info)",
warning: "var(--color-warning)",
tip: "var(--color-tip)",
green: {
DEFAULT: "var(--color-green)",
green,
cyan,
red,
yellow,
purple,
orange,
muted: "var(--color-muted)",
strong: "var(--color-strong)",
primary: {
DEFAULT: "var(--color-primary)",
transparent: "var(--color-transparent-primary)",
dark: "var(--color-primary-dark)",
light:
"lch(from var(--color-primary) calc(l + 10) calc(c + 1) calc(h - 5))",
brightLight:
"lch(from var(--color-primary) calc(l - 1) calc(c + 20) calc(h + 5))",
brightDark:
"lch(from var(--color-primary) calc(l - 6) calc(c + 20) calc(h + 5))",
},
success: {
DEFAULT: "var(--color-success)",
transparent: "lch(from var(--color-success) l c h / 0.3)",
dark: "lch(from var(--color-success) calc(l - 7) calc(c - 1) calc(h + 5))",
light:
"lch(from var(--color-success) calc(l + 4) calc(c + 1) calc(h - 5))",
brightLight:
"lch(from var(--color-success) calc(l - 1) calc(c + 20) calc(h + 10))",
brightDark:
"lch(from var(--color-success) calc(l - 6) calc(c + 20) calc(h + 10))",
},
info: {
DEFAULT: "var(--color-info)",
transparent: "lch(from var(--color-info) l c h / 0.3)",
dark: "lch(from var(--color-info) calc(l - 7) calc(c - 1) calc(h + 5))",
light:
"lch(from var(--color-info) calc(l + 4) calc(c + 1) calc(h - 5))",
brightLight:
"lch(from var(--color-info) calc(l - 1) calc(c + 20) calc(h + 5))",
brightDark:
"lch(from var(--color-info) calc(l - 4) calc(c + 20) calc(h + 5))",
},
warning: {
DEFAULT: "var(--color-warning)",
transparent: "lch(from var(--color-warning) l c h / 0.3)",
dark: "lch(from var(--color-warning) calc(l - 7) calc(c - 1) calc(h + 5))",
light:
"lch(from var(--color-warning) calc(l + 4) calc(c + 1) calc(h - 5))",
brightLight:
"lch(from var(--color-warning) calc(l - 1) calc(c + 30) calc(h + 15))",
brightDark:
"lch(from var(--color-warning) calc(l - 4) calc(c + 30) calc(h + 15))",
},
danger: {
DEFAULT: "var(--color-danger)",
transparent: "lch(from var(--color-danger) l c h / 0.3)",
dark: "lch(from var(--color-danger) calc(l - 7) calc(c - 1) calc(h + 5))",
light:
"lch(from var(--color-danger) calc(l + 4) calc(c + 1) calc(h - 5))",
brightLight:
"lch(from var(--color-danger) calc(l - 2) calc(c + 20) calc(h + 10))",
brightDark:
"lch(from var(--color-danger) calc(l - 6) calc(c + 10) calc(h + 10))",
},
tip: {
DEFAULT: "var(--color-tip)",
transparent: "lch(from var(--color-tip) l c h / 0.3)",
dark: "lch(from var(--color-tip) calc(l - 7) calc(c - 1) calc(h + 5))",
light:
"lch(from var(--color-tip) calc(l + 4) calc(c + 1) calc(h - 5))",
brightLight:
"lch(from var(--color-tip) calc(l - 1) calc(c + 20) calc(h + 10))",
brightDark:
"lch(from var(--color-tip) calc(l - 4) calc(c + 20) calc(h + 10))",
},
alert: {
DEFAULT: "var(--color-alert)",
transparent: "lch(from var(--color-alert) l c h / 0.3)",
dark: "lch(from var(--color-alert) calc(l - 7) calc(c - 1) calc(h + 5))",
light:
"lch(from var(--color-alert) calc(l + 4) calc(c + 1) calc(h - 5))",
brightLight:
"lch(from var(--color-alert) calc(l - 1) calc(c + 50) calc(h + 15))",
brightDark:
"lch(from var(--color-alert) calc(l - 5) calc(c + 50) calc(h + 15))",
},
},
textColor: {
default: "var(--color-default)",
highlight: "var(--color-highlight)",
strong: "var(--color-strong)",
muted: "var(--color-muted)",
},
borderColor: {
DEFAULT: "var(--color-border-default)",
},
backgroundColor: {
highlight: "var(--color-transparent-primary)",
muted: "var(--color-bg-muted)",
highlight: "var(--color-background-highlight)",
},
fontFamily: {
display: ["var(--font-manrope)"],
@@ -195,4 +301,10 @@ const config = {
),
],
};
const config = {
presets: [preset],
darkMode: ["class"],
content: ["./src/**/*.{js,ts,jsx,tsx,mdx}"],
};
export default config;

View File

@@ -4,7 +4,12 @@ import { GcmpLogo } from "@garden-co/design-system/src/components/atoms/logos/Gc
import { Nav } from "@garden-co/design-system/src/components/organisms/Nav";
export function GcmpNav() {
const cta = (
<Button variant="secondary" className="ml-3" href="mailto:hello@garden.co">
<Button
intent="success"
variant="outline"
size="sm"
href="mailto:hello@garden.co"
>
Contact us
</Button>
);

View File

@@ -31,7 +31,11 @@ export default function Products() {
<div className="flex items-center justify-between gap-4">
<JazzLogo className="h-10 w-auto" />
<div>
<Button href="https://jazz.tools" variant="secondary">
<Button
href="https://jazz.tools"
intent="primary"
variant="outline"
>
Go to jazz.tools
</Button>
</div>

View File

@@ -1,7 +1,7 @@
import type { Config } from "tailwindcss";
const config: Config = {
presets: [require("@garden-co/design-system/tailwind.config.js")],
presets: [require("@garden-co/design-system/tailwind.config.js").preset],
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",

View File

@@ -13,7 +13,8 @@ export function FakeGetStartedButton({ tier }: { tier: "starter" | "indie" }) {
);
window.location.pathname = "/docs";
}}
variant={tier === "starter" ? "secondary" : "primary"}
intent="primary"
variant={tier === "starter" ? "outline" : undefined}
>
Get {tier === "starter" ? "Starter" : "Indie"} API Key
</Button>

View File

@@ -180,7 +180,7 @@ export function Pricing() {
</ul>
</div>
<Button href="https://cal.com/anselm-io/cloud-pro-intro">
<Button href="https://cal.com/anselm-io/cloud-pro-intro" intent="primary">
Schedule intro call
</Button>

View File

@@ -37,7 +37,10 @@ export function SideNavSectionList({ items }: { items?: SideNavItem[] }) {
{items.map(({ name, href, items, done }) => (
<li key={name}>
<SideNavItem href={href}>
<span className={done === 0 ? "text-muted" : ""}>{name}</span>
<span className={done === 0 ? "text-muted" : ""}>
{name}
{done === 0 && <span className="text-stone-800 text-[0.5rem]"> (docs coming soon)</span>}
</span>
</SideNavItem>
</li>
))}

View File

@@ -31,10 +31,11 @@ export function FrameworkSelect() {
<DropdownButton
className="w-full justify-between"
as={Button}
variant="secondary"
variant="outline"
intent="default"
>
{frameworkNames[selectedFramework].label}
<Icon name="chevronDown" size="sm" className="text-muted" />
<Icon name="chevronDown" size="sm" />
</DropdownButton>
<DropdownMenu className="w-[--button-width] z-50" anchor="bottom start">
{Object.entries(frameworkNames)

View File

@@ -19,9 +19,6 @@ export function HelpLinks({ className }: { className?: string }) {
}
}, []);
const linkClassName =
"inline-flex items-center gap-2 py-1 text-sm text-stone-600 dark:text-stone-400 hover:text-highlight";
return (
<div
className={clsx(
@@ -29,15 +26,16 @@ export function HelpLinks({ className }: { className?: string }) {
className,
)}
>
<Button href={issueUrl} variant="plain" newTab className={linkClassName}>
<Button href={issueUrl} intent="strong" variant="ghost" newTab>
<SiGithub className="size-4" />
Docs issue?
</Button>
<Button
href="https://discord.gg/utDMjHYg42"
variant="plain"
intent="strong"
variant="ghost"
newTab
className={linkClassName}
className="lg:mt-3"
>
<SiDiscord className="size-4" />
Join Discord

View File

@@ -25,16 +25,16 @@ export function ExampleLinks({ example }: { example: Example }) {
return (
<>
<div className="flex gap-2">
<Button variant="secondary" size="sm" onClick={() => setIsOpen(true)}>
<Button intent="primary" variant="inverted" size="sm" onClick={() => setIsOpen(true)}>
Use as template
</Button>
<Button href={githubUrl} newTab variant="secondary" size="sm">
<Button href={githubUrl} newTab intent="primary" variant="inverted" size="sm">
<span className="md:hidden">Code</span>
<span className="hidden md:inline">View code</span>
</Button>
{demoUrl && (
<Button href={demoUrl} newTab variant="secondary" size="sm">
<Button href={demoUrl} newTab intent="primary" variant="inverted" size="sm">
<span className="md:hidden">Demo</span>
<span className="hidden md:inline">View demo</span>
</Button>

View File

@@ -6,7 +6,10 @@ import { Testimonial } from "@garden-co/design-system/src/components/molecules/T
function TheoTestimonial({
size,
className,
}: { size?: "sm" | "md"; className?: string }) {
}: {
size?: "sm" | "md";
className?: string;
}) {
return (
<Testimonial
size={size}
@@ -31,7 +34,7 @@ function TheoTestimonial({
export function EarlyAdopterSection() {
return (
<div className="grid grid grid-cols-3 items-center gap-y-12">
<div className="grid grid-cols-3 items-center gap-y-12">
<TheoTestimonial size="md" className="col-span-3 lg:hidden" />
<div className="col-span-3 lg:col-span-2">
<div className="max-w-3xl space-y-6">
@@ -53,10 +56,14 @@ export function EarlyAdopterSection() {
</Prose>
<div className="flex gap-3">
<Button href="/docs" variant="primary">
<Button href="/docs" intent="primary">
Read docs
</Button>
<Button href="https://discord.gg/utDMjHYg42" variant="secondary">
<Button
href="https://discord.gg/utDMjHYg42"
intent="primary"
variant="outline"
>
Join Discord
</Button>
</div>

View File

@@ -51,7 +51,9 @@ function Illustration() {
<Icon
name="encryption"
size="3xl"
className="z-30 text-primary p-1.5 rounded-lg bg-highlight dark:bg-stone-900"
className="z-30 p-1.5 rounded-lg"
intent="primary"
hasBackground
/>
{/*<LockKeyholeIcon*/}
@@ -72,7 +74,7 @@ function Illustration() {
export function EncryptionSection() {
return (
<Card className="overflow-hidden dark:bg-stone-925">
<div className="flex grid md:grid-cols-3 md:gap-3">
<div className="grid md:grid-cols-3 md:gap-3">
<div className="md:col-span-2 px-4 pb-4 md:p-8">
<H3 className="mb-0 text-balance">
End-to-end encrypted and tamper-proof

View File

@@ -139,7 +139,7 @@ export function FeaturesSection() {
</p>
</Prose>
<div className="flex items-center flex-wrap gap-3">
<Button href="/cloud" variant="primary">
<Button href="/cloud" intent="primary">
View free tier & pricing
</Button>
@@ -161,8 +161,8 @@ export function FeaturesSection() {
key={feature}
className="flex items-center gap-1.5 whitespace-nowrap"
>
<span className="text-primary p-1 rounded-full bg-highlight">
<Icon name="check" size="xs" />
<span className="p-1 rounded-full bg-primary-transparent">
<Icon name="check" size="xs" intent="primary" />
</span>
{feature}
</li>

View File

@@ -86,8 +86,8 @@ export function HeroSection() {
key={title}
className="flex text-xs sm:text-sm gap-2 items-center"
>
<span className="text-primary p-1.5 rounded-lg bg-highlight dark:bg-stone-900">
<Icon size="xs" name={icon} />
<span className="p-1.5 rounded-lg bg-primary-transparent">
<Icon size="xs" name={icon} intent="primary" />
</span>
<p>{title}</p>
</div>

View File

@@ -63,9 +63,8 @@ function Step({
<div className="flex gap-3 px-4 sm:px-6">
<p
className={clsx(
"bg-highlight size-6 rounded-full text-primary text-sm font-semibold font-mono",
"bg-primary-transparent text-primary size-6 rounded-full text-sm font-semibold font-mono",
"inline-flex items-center justify-center text-center shrink-0",
"dark:bg-blue dark:text-white",
)}
>
<span className="sr-only">Step</span>

View File

@@ -15,15 +15,16 @@ export function QuickSearch() {
return (
<Button
className="group xl:min-w-48 text-stone-600 md:mr-5"
variant="secondary"
className="group xl:min-w-48 md:mr-5"
intent="muted"
variant="outline"
onClick={() => setOpen((open) => !open)}
>
<Icon name="search" size="xs" className=" text-stone-600" />
<span className="font-normal flex-1 text-left text-sm text-stone-600 group-hover:text-blue xl:not-sr-only">
<Icon name="search" size="xs" intent="default" />
<span className="font-normal flex-1 text-left text-sm xl:not-sr-only">
Search docs
</span>
<kbd className="hidden gap-0.5 xl:text-sm text-stone-600 lg:inline-flex">
<kbd className="hidden gap-0.5 xl:text-sm lg:inline-flex">
<kbd className="font-sans">{isMac ? "⌘" : "Ctrl"}</kbd>
<kbd className="font-sans">K</kbd>
</kbd>

View File

@@ -118,7 +118,7 @@ To use it, install the following Packages:
<CodeGroup>
```bash
pnpm add react-native-quick-crypto@1.0.0-beta.18 react-native-nitro-modules
pnpm add react-native-quick-crypto@1.0.0-beta.18 react-native-nitro-modules react-native-fast-encoder
```
</CodeGroup>

View File

@@ -116,7 +116,7 @@ To use it, install the following Packages:
<CodeGroup>
```bash
pnpm add react-native-quick-crypto@1.0.0-beta.18 react-native-nitro-modules
pnpm add react-native-quick-crypto@1.0.0-beta.18 react-native-nitro-modules react-native-fast-encoder
```
</CodeGroup>

View File

@@ -40,7 +40,7 @@ npx expo prebuild
<CodeGroup>
```bash
# Expo dependencies
npx expo install expo-linking expo-secure-store expo-file-system @react-native-community/netinfo @bam.tech/react-native-image-resizer
npx expo install expo-linking expo-secure-store expo-sqlite expo-file-system @react-native-community/netinfo @bam.tech/react-native-image-resizer
# React Native polyfills
npm i -S @azure/core-asynciterator-polyfill react-native-url-polyfill readable-stream react-native-get-random-values

View File

@@ -7,7 +7,7 @@ export const colourSafelist = Object.values(pingColorMap).flatMap((value) => {
});
const config: Config = {
presets: [require("@garden-co/design-system/tailwind.config.js")],
presets: [require("@garden-co/design-system/tailwind.config.js").preset],
darkMode: ["class"],
content: [
"./pages/**/*.{js,ts,jsx,tsx,mdx}",

View File

@@ -65,6 +65,9 @@ importers:
react-dom:
specifier: 'catalog:'
version: 19.0.0(react@19.0.0)
react-hot-toast:
specifier: ^2.5.2
version: 2.5.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
resend:
specifier: ^4.0.0
version: 4.5.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
@@ -3415,6 +3418,11 @@ packages:
resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==}
engines: {node: '>=4'}
goober@2.1.16:
resolution: {integrity: sha512-erjk19y1U33+XAMe1VTvIONHYoSqE4iS7BYUZfHaqeohLmnC0FdxEh7rQU+6MZ4OajItzjZFSRtVANrQwNq6/g==}
peerDependencies:
csstype: ^3.0.10
graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
@@ -4421,6 +4429,13 @@ packages:
peerDependencies:
react: ^19.0.0
react-hot-toast@2.5.2:
resolution: {integrity: sha512-Tun3BbCxzmXXM7C+NI4qiv6lT0uwGh4oAfeJyNOjYUejTsm35mK9iCaYLGv8cBz9L5YxZLx/2ii7zsIwPtPUdw==}
engines: {node: '>=10'}
peerDependencies:
react: '>=16'
react-dom: '>=16'
react-is@18.3.1:
resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==}
@@ -9166,6 +9181,10 @@ snapshots:
globals@11.12.0: {}
goober@2.1.16(csstype@3.1.3):
dependencies:
csstype: 3.1.3
graceful-fs@4.2.11: {}
has-flag@4.0.0: {}
@@ -10723,6 +10742,13 @@ snapshots:
react: 19.0.0
scheduler: 0.25.0
react-hot-toast@2.5.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0):
dependencies:
csstype: 3.1.3
goober: 2.1.16(csstype@3.1.3)
react: 19.0.0
react-dom: 19.0.0(react@19.0.0)
react-is@18.3.1: {}
react-native@0.79.3(@babel/core@7.27.4)(@types/react@19.0.0)(react@19.0.0):

View File

@@ -23,12 +23,12 @@
"playwright": "^1.50.1",
"turbo": "^2.3.1",
"typedoc": "^0.25.13",
"vitest": "catalog:",
"yalc": "^1.0.0-pre.53"
"vitest": "catalog:"
},
"scripts": {
"dev": "turbo dev",
"build": "turbo build && cd homepage/homepage && turbo build",
"build:packages": "turbo build --filter='./packages/*'",
"lint": "turbo lint && cd homepage/homepage && pnpm run lint",
"test": "vitest",
"test:ci": "vitest run --watch=false --coverage.enabled=true",
@@ -39,8 +39,7 @@
"changeset-version": "changeset version && pnpm i --no-frozen-lockfile",
"release": "turbo run build --filter='./packages/*' && pnpm changeset publish && git push --follow-tags",
"clean": "rm -rf ./packages/*/dist && rm -rf ./packages/*/node_modules && rm -rf ./examples/*/node_modules && rm -rf ./examples/*/dist",
"check-catalog-deps": "node scripts/check-catalog-deps.js",
"yalc:all": "for d in packages/*/; do echo $d; cd $d; pnpm yalc push --replace --sig; cd '../../'; done"
"check-catalog-deps": "node scripts/check-catalog-deps.js"
},
"version": "0.0.0",
"pnpm": {

View File

@@ -1,5 +1,35 @@
# cojson-storage-indexeddb
## 0.15.6
### Patch Changes
- cojson@0.15.6
- cojson-storage@0.15.6
## 0.15.5
### Patch Changes
- cojson@0.15.5
- cojson-storage@0.15.5
## 0.15.4
### Patch Changes
- Updated dependencies [277e4d4]
- cojson@0.15.4
- cojson-storage@0.15.4
## 0.15.3
### Patch Changes
- 535c460: Refresh the IndexedDB transaction when getting "Failed to execute 'objectStore' on 'IDBTransaction': The transaction has finished"
- cojson@0.15.3
- cojson-storage@0.15.3
## 0.15.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "cojson-storage-indexeddb",
"version": "0.15.2",
"version": "0.15.6",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",

View File

@@ -9,7 +9,7 @@ export type StoreName =
// in a single transaction.
export class CoJsonIDBTransaction {
db: IDBDatabase;
tx: IDBTransaction;
declare tx: IDBTransaction;
pendingRequests: ((txEntry: this) => void)[] = [];
rejectHandlers: (() => void)[] = [];
@@ -23,6 +23,10 @@ export class CoJsonIDBTransaction {
constructor(db: IDBDatabase) {
this.db = db;
this.refresh();
}
refresh() {
this.tx = this.db.transaction(
["coValues", "sessions", "transactions", "signatureAfter"],
"readwrite",
@@ -43,7 +47,12 @@ export class CoJsonIDBTransaction {
}
getObjectStore(name: StoreName) {
return this.tx.objectStore(name);
try {
return this.tx.objectStore(name);
} catch (error) {
this.refresh();
return this.tx.objectStore(name);
}
}
private pushRequest<T>(

View File

@@ -1,5 +1,34 @@
# cojson-storage-sqlite
## 0.15.6
### Patch Changes
- cojson@0.15.6
- cojson-storage@0.15.6
## 0.15.5
### Patch Changes
- cojson@0.15.5
- cojson-storage@0.15.5
## 0.15.4
### Patch Changes
- Updated dependencies [277e4d4]
- cojson@0.15.4
- cojson-storage@0.15.4
## 0.15.3
### Patch Changes
- cojson@0.15.3
- cojson-storage@0.15.3
## 0.15.2
### Patch Changes

View File

@@ -1,13 +1,13 @@
{
"name": "cojson-storage-sqlite",
"type": "module",
"version": "0.15.2",
"version": "0.15.6",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "MIT",
"dependencies": {
"better-sqlite3": "^11.7.0",
"cojson": "workspace:0.15.2",
"cojson": "workspace:0.15.6",
"cojson-storage": "workspace:*"
},
"devDependencies": {

View File

@@ -1,5 +1,30 @@
# cojson-storage
## 0.15.6
### Patch Changes
- cojson@0.15.6
## 0.15.5
### Patch Changes
- cojson@0.15.5
## 0.15.4
### Patch Changes
- Updated dependencies [277e4d4]
- cojson@0.15.4
## 0.15.3
### Patch Changes
- cojson@0.15.3
## 0.15.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "cojson-storage",
"version": "0.15.2",
"version": "0.15.6",
"main": "dist/index.js",
"type": "module",
"types": "dist/index.d.ts",

View File

@@ -1,5 +1,30 @@
# cojson-transport-nodejs-ws
## 0.15.6
### Patch Changes
- cojson@0.15.6
## 0.15.5
### Patch Changes
- cojson@0.15.5
## 0.15.4
### Patch Changes
- Updated dependencies [277e4d4]
- cojson@0.15.4
## 0.15.3
### Patch Changes
- cojson@0.15.3
## 0.15.2
### Patch Changes

View File

@@ -1,7 +1,7 @@
{
"name": "cojson-transport-ws",
"type": "module",
"version": "0.15.2",
"version": "0.15.6",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"license": "MIT",

View File

@@ -1,5 +1,17 @@
# cojson
## 0.15.6
## 0.15.5
## 0.15.4
### Patch Changes
- 277e4d4: Fix sync with circular deps
## 0.15.3
## 0.15.2
### Patch Changes

View File

@@ -25,7 +25,7 @@
},
"type": "module",
"license": "MIT",
"version": "0.15.2",
"version": "0.15.6",
"devDependencies": {
"@opentelemetry/sdk-metrics": "^2.0.0",
"typescript": "catalog:"

View File

@@ -207,7 +207,17 @@ export class SyncManager {
}
}
sendNewContentIncludingDependencies(id: RawCoID, peer: PeerState) {
sendNewContentIncludingDependencies(
id: RawCoID,
peer: PeerState,
seen: Set<RawCoID> = new Set(),
) {
if (seen.has(id)) {
return;
}
seen.add(id);
const coValue = this.local.getCoValue(id);
if (!coValue.isAvailable()) {
@@ -215,7 +225,7 @@ export class SyncManager {
}
for (const dependency of coValue.getDependedOnCoValues()) {
this.sendNewContentIncludingDependencies(dependency, peer);
this.sendNewContentIncludingDependencies(dependency, peer, seen);
}
const newContentPieces = coValue.verified.newContentSince(
@@ -494,7 +504,7 @@ export class SyncManager {
)) {
const dependencyCoValue = this.local.getCoValue(dependency);
if (!dependencyCoValue.isAvailable()) {
if (!dependencyCoValue.verified) {
coValue.markMissingDependency(dependency);
if (!dependencyCoValue.verified) {

View File

@@ -826,4 +826,46 @@ describe("loading coValues from server", () => {
]
`);
});
test("coValue with circular deps loading", async () => {
const client = setupTestNode({
connected: true,
});
const group = client.node.createGroup();
const parentGroup = client.node.createGroup();
parentGroup.addMember("everyone", "reader");
group.extend(parentGroup);
// Disable the circular dependency check in the extend function
vi.spyOn(parentGroup, "isSelfExtension").mockImplementation(() => false);
parentGroup.extend(group);
const map = group.createMap();
map.set("hello", "world");
await map.core.waitForSync();
expect(
SyncMessagesLog.getMessages({
ParentGroup: parentGroup.core,
Group: group.core,
Map: map.core,
}),
).toMatchInlineSnapshot(`
[
"client -> server | CONTENT ParentGroup header: true new: After: 0 New: 8",
"server -> client | LOAD Group sessions: empty",
"client -> server | CONTENT Group header: true new: After: 0 New: 6",
"server -> client | KNOWN ParentGroup sessions: empty",
"client -> server | CONTENT Group header: true new: After: 0 New: 6",
"server -> client | KNOWN Group sessions: header/6",
"client -> server | CONTENT Map header: true new: After: 0 New: 1",
"server -> client | KNOWN Group sessions: header/6",
"server -> client | KNOWN Map sessions: header/1",
]
`);
});
});

View File

@@ -1,5 +1,11 @@
# create-jazz-app
## 0.1.25
### Patch Changes
- b774bb3: Add default project name
## 0.1.24
### Patch Changes

View File

@@ -5,7 +5,7 @@
"types": "src/index.ts",
"type": "module",
"license": "MIT",
"version": "0.1.24",
"version": "0.1.25",
"bin": {
"create-jazz-app": "./dist/index.js"
},

View File

@@ -493,10 +493,24 @@ async function promptUser(
}
if (!partialOptions.projectName) {
// Determine a default project name if possible
let defaultProjectName = undefined;
if (partialOptions.example) {
// Use the example name, affixed with -app if not already
defaultProjectName = partialOptions.example.endsWith("-app")
? partialOptions.example
: `${partialOptions.example}-app`;
} else if (partialOptions.starter) {
// Use the starter name, affixed with -app if not already
defaultProjectName = partialOptions.starter.endsWith("-app")
? partialOptions.starter
: `${partialOptions.starter}-app`;
}
questions.push({
type: "input",
name: "projectName",
message: chalk.cyan("Enter your project name:"),
default: defaultProjectName,
validate: (input: string) =>
input ? true : chalk.red("Project name cannot be empty"),
});
@@ -590,6 +604,17 @@ program
} else if (directory && !options.projectName) {
// If directory is provided but not project name, use directory as project name
partialOptions.projectName = directory;
} else if (!directory && !options.projectName) {
// If no directory or projectName, and example is provided, use example as default
if (options.example) {
partialOptions.projectName = options.example.endsWith("-app")
? options.example
: `${options.example}-app`;
} else if (options.starter) {
partialOptions.projectName = options.starter.endsWith("-app")
? options.starter
: `${options.starter}-app`;
}
}
if (options.starter)

View File

@@ -1,5 +1,44 @@
# jazz-auth-betterauth
## 0.15.6
### Patch Changes
- Updated dependencies [a5ceaff]
- jazz-tools@0.15.6
- jazz-betterauth-client-plugin@0.15.6
- cojson@0.15.6
## 0.15.5
### Patch Changes
- 4b89838: Changes that verify the BetterAuth package for React also works with React Native and Expo apps
- Updated dependencies [23bfea5]
- Updated dependencies [e4ba23c]
- Updated dependencies [4b89838]
- jazz-tools@0.15.5
- jazz-betterauth-client-plugin@0.15.5
- cojson@0.15.5
## 0.15.4
### Patch Changes
- Updated dependencies [277e4d4]
- cojson@0.15.4
- jazz-tools@0.15.4
- jazz-betterauth-client-plugin@0.15.4
## 0.15.3
### Patch Changes
- Updated dependencies [45f73a7]
- jazz-tools@0.15.3
- jazz-betterauth-client-plugin@0.15.3
- cojson@0.15.3
## 0.15.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-auth-betterauth",
"version": "0.15.2",
"version": "0.15.6",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -9,29 +9,33 @@ import {
} from "jazz-tools";
import type { AuthSetPayload } from "jazz-tools";
export const newAuthClient = <T extends ClientOptions>(options?: T) => {
type Plugins = Array<
| NonNullable<NonNullable<T>["plugins"]>[number]
| ReturnType<typeof jazzClientPlugin>
type Plugins<T extends ClientOptions> = Array<
| NonNullable<NonNullable<T>["plugins"]>[number]
| ReturnType<typeof jazzClientPlugin>
>;
type Options<T extends ClientOptions> = {
plugins: Plugins<T>;
} & T;
export type AuthClient<T extends ClientOptions> = ReturnType<
typeof createAuthClient<Options<T>>
> &
ReturnType<
typeof createAuthClient<{
plugins: [ReturnType<typeof jazzClientPlugin>];
}>
>;
type Options<T> = {
plugins: Plugins;
} & T;
type AuthClient<T> = ReturnType<typeof createAuthClient<Options<T>>> &
ReturnType<
typeof createAuthClient<{
plugins: [ReturnType<typeof jazzClientPlugin>];
}>
>;
export const newAuthClient = <T extends ClientOptions>(
options?: T,
): AuthClient<T> => {
return createAuthClient<Options<T>>({
...options,
plugins: [...(options?.plugins ?? []), ...[jazzClientPlugin()]],
} as Options<T>) as AuthClient<T>;
};
export type AuthClient<T extends ClientOptions> = ReturnType<
typeof newAuthClient<T>
>;
export type InferredSession<T extends ClientOptions> =
AuthClient<T>["$Infer"]["Session"];
export type Session<T extends ClientOptions> =

View File

@@ -1,5 +1,29 @@
# jazz-betterauth-client-plugin
## 0.15.6
### Patch Changes
- jazz-betterauth-server-plugin@0.15.6
## 0.15.5
### Patch Changes
- jazz-betterauth-server-plugin@0.15.5
## 0.15.4
### Patch Changes
- jazz-betterauth-server-plugin@0.15.4
## 0.15.3
### Patch Changes
- jazz-betterauth-server-plugin@0.15.3
## 0.15.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-betterauth-client-plugin",
"version": "0.15.2",
"version": "0.15.6",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -1,5 +1,39 @@
# jazz-betterauth-server-plugin
## 0.15.6
### Patch Changes
- Updated dependencies [a5ceaff]
- jazz-tools@0.15.6
- cojson@0.15.6
## 0.15.5
### Patch Changes
- Updated dependencies [23bfea5]
- Updated dependencies [e4ba23c]
- Updated dependencies [4b89838]
- jazz-tools@0.15.5
- cojson@0.15.5
## 0.15.4
### Patch Changes
- Updated dependencies [277e4d4]
- cojson@0.15.4
- jazz-tools@0.15.4
## 0.15.3
### Patch Changes
- Updated dependencies [45f73a7]
- jazz-tools@0.15.3
- cojson@0.15.3
## 0.15.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-betterauth-server-plugin",
"version": "0.15.2",
"version": "0.15.6",
"type": "module",
"main": "dist/index.js",
"types": "src/index.ts",

View File

@@ -1,5 +1,48 @@
# jazz-react-auth-betterauth
## 0.15.6
### Patch Changes
- Updated dependencies [a5ceaff]
- jazz-tools@0.15.6
- jazz-auth-betterauth@0.15.6
- jazz-betterauth-client-plugin@0.15.6
- cojson@0.15.6
## 0.15.5
### Patch Changes
- 4b89838: Changes that verify the BetterAuth package for React also works with React Native and Expo apps
- Updated dependencies [23bfea5]
- Updated dependencies [e4ba23c]
- Updated dependencies [4b89838]
- jazz-tools@0.15.5
- jazz-auth-betterauth@0.15.5
- jazz-betterauth-client-plugin@0.15.5
- cojson@0.15.5
## 0.15.4
### Patch Changes
- Updated dependencies [277e4d4]
- cojson@0.15.4
- jazz-auth-betterauth@0.15.4
- jazz-tools@0.15.4
- jazz-betterauth-client-plugin@0.15.4
## 0.15.3
### Patch Changes
- Updated dependencies [45f73a7]
- jazz-tools@0.15.3
- jazz-auth-betterauth@0.15.3
- jazz-betterauth-client-plugin@0.15.3
- cojson@0.15.3
## 0.15.2
### Patch Changes

View File

@@ -1,6 +1,6 @@
{
"name": "jazz-react-auth-betterauth",
"version": "0.15.2",
"version": "0.15.6",
"type": "module",
"main": "dist/index.js",
"types": "src/index.tsx",

View File

@@ -1,6 +1,5 @@
"use client";
import type { ClientOptions } from "better-auth/client";
import { createContext, useContext } from "react";
import { useBetterAuth } from "../index.js";

View File

@@ -4,7 +4,7 @@ import {
useAuthSecretStorage,
useIsAuthenticated,
useJazzContext,
} from "jazz-tools/react";
} from "jazz-tools/react-core";
import { useEffect, useMemo, useState } from "react";
export * from "./contexts/Auth.js";

View File

@@ -1,5 +1,47 @@
# jazz-run
## 0.15.6
### Patch Changes
- Updated dependencies [a5ceaff]
- jazz-tools@0.15.6
- cojson@0.15.6
- cojson-storage-sqlite@0.15.6
- cojson-transport-ws@0.15.6
## 0.15.5
### Patch Changes
- Updated dependencies [23bfea5]
- Updated dependencies [e4ba23c]
- Updated dependencies [4b89838]
- jazz-tools@0.15.5
- cojson@0.15.5
- cojson-storage-sqlite@0.15.5
- cojson-transport-ws@0.15.5
## 0.15.4
### Patch Changes
- Updated dependencies [277e4d4]
- cojson@0.15.4
- cojson-storage-sqlite@0.15.4
- cojson-transport-ws@0.15.4
- jazz-tools@0.15.4
## 0.15.3
### Patch Changes
- Updated dependencies [45f73a7]
- jazz-tools@0.15.3
- cojson@0.15.3
- cojson-storage-sqlite@0.15.3
- cojson-transport-ws@0.15.3
## 0.15.2
### Patch Changes

View File

@@ -3,7 +3,7 @@
"bin": "./dist/index.js",
"type": "module",
"license": "MIT",
"version": "0.15.2",
"version": "0.15.6",
"exports": {
"./startSyncServer": {
"import": "./dist/startSyncServer.js",
@@ -28,11 +28,11 @@
"@effect/printer-ansi": "^0.34.5",
"@effect/schema": "^0.71.1",
"@effect/typeclass": "^0.25.5",
"cojson": "workspace:0.15.2",
"cojson-storage-sqlite": "workspace:0.15.2",
"cojson-transport-ws": "workspace:0.15.2",
"cojson": "workspace:0.15.6",
"cojson-storage-sqlite": "workspace:0.15.6",
"cojson-transport-ws": "workspace:0.15.6",
"effect": "^3.6.5",
"jazz-tools": "workspace:0.15.2",
"jazz-tools": "workspace:0.15.6",
"ws": "^8.14.2"
},
"devDependencies": {

View File

@@ -1,5 +1,48 @@
# jazz-tools
## 0.15.6
### Patch Changes
- a5ceaff: add Group.getDirectMembers to get only direct members of a group
- cojson@0.15.6
- cojson-storage@0.15.6
- cojson-storage-indexeddb@0.15.6
- cojson-transport-ws@0.15.6
## 0.15.5
### Patch Changes
- 23bfea5: Make all React Native dependencies in `jazz-tools` optional peer dependencies
- e4ba23c: Fix type error with `RNQuickCrypto` that prevented using it as a `CryptoProvider`
- 4b89838: Changes that verify the BetterAuth package for React also works with React Native and Expo apps
- cojson@0.15.5
- cojson-storage@0.15.5
- cojson-storage-indexeddb@0.15.5
- cojson-transport-ws@0.15.5
## 0.15.4
### Patch Changes
- Updated dependencies [277e4d4]
- cojson@0.15.4
- cojson-storage@0.15.4
- cojson-storage-indexeddb@0.15.4
- cojson-transport-ws@0.15.4
## 0.15.3
### Patch Changes
- 45f73a7: fix image `originalSize` to be coherent with the highest resolution
- Updated dependencies [535c460]
- cojson-storage-indexeddb@0.15.3
- cojson@0.15.3
- cojson-storage@0.15.3
- cojson-transport-ws@0.15.3
## 0.15.2
### Patch Changes

View File

@@ -139,10 +139,9 @@
},
"type": "module",
"license": "MIT",
"version": "0.15.2",
"version": "0.15.6",
"dependencies": {
"@manuscripts/prosemirror-recreate-steps": "^0.1.4",
"@op-engineering/op-sqlite": "^11.4.8",
"@scure/base": "1.2.1",
"@scure/bip39": "^1.3.0",
"@tiptap/core": "^2.12.0",
@@ -152,7 +151,6 @@
"cojson-storage": "workspace:*",
"cojson-storage-indexeddb": "workspace:*",
"cojson-transport-ws": "workspace:*",
"expo-sqlite": "15.2.9",
"fast-myers-diff": "^3.2.0",
"goober": "^2.1.16",
"image-blob-reduce": "^4.1.0",
@@ -163,10 +161,6 @@
"prosemirror-schema-basic": "^1.2.2",
"prosemirror-state": "^1.4.3",
"prosemirror-transform": "^1.9.0",
"react-native-fast-encoder": "^0.2.0",
"react-native-mmkv": "^3.2.0",
"react-native-nitro-modules": "0.25.2",
"react-native-quick-crypto": "1.0.0-beta.16",
"zod": "3.25.28"
},
"scripts": {
@@ -187,6 +181,8 @@
"@testing-library/react": "16.2.0",
"@types/react": "19.0.0",
"@types/react-dom": "19.0.0",
"@vitest/browser": "^3.2.4",
"playwright": "^1.50.1",
"tsup": "8.5.0",
"typescript": "catalog:",
"vitest": "catalog:",
@@ -194,27 +190,39 @@
},
"peerDependencies": {
"@bam.tech/react-native-image-resizer": "*",
"@op-engineering/op-sqlite": "^11.4.8",
"@react-native-community/netinfo": "*",
"expo-file-system": "*",
"expo-secure-store": "*",
"expo-sqlite": "15.2.9",
"react": "*",
"react-dom": "*",
"react-native": "*",
"react-native-fast-encoder": "^0.2.0",
"react-native-mmkv": "^3.2.0",
"react-native-nitro-modules": "0.25.2",
"react-native-quick-crypto": "1.0.0-beta.16",
"svelte": "^5.0.0"
},
"peerDependenciesMeta": {
"@bam.tech/react-native-image-resizer": {
"optional": true
},
"expo-file-system": {
"@op-engineering/op-sqlite": {
"optional": true
},
"@react-native-community/netinfo": {
"optional": true
},
"expo-file-system": {
"optional": true
},
"expo-secure-store": {
"optional": true
},
"expo-sqlite": {
"optional": true
},
"react": {
"optional": true
},
@@ -224,8 +232,20 @@
"react-native": {
"optional": true
},
"react-native-fast-encoder": {
"optional": true
},
"react-native-mmkv": {
"optional": true
},
"react-native-nitro-modules": {
"optional": true
},
"react-native-quick-crypto": {
"optional": true
},
"svelte": {
"optional": true
}
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -8,7 +8,7 @@ import {
} from "jazz-tools";
import Pica from "pica";
let pica: Pica.Pica | undefined;
let reducer: ImageBlobReduce.ImageBlobReduce | undefined;
/** @category Image creation */
export async function createImage(
@@ -18,113 +18,114 @@ export async function createImage(
maxSize?: 256 | 1024 | 2048;
},
): Promise<Loaded<typeof ImageDefinition>> {
// Inizialize Pica here to not have module side effects
if (!pica) {
pica = new Pica();
}
// Get the original size of the image
const { width: originalWidth, height: originalHeight } =
await getImageSize(imageBlobOrFile);
let originalWidth!: number;
let originalHeight!: number;
const Reducer = new ImageBlobReduce({ pica });
Reducer.after("_blob_to_image", (env) => {
originalWidth =
(env as unknown as { orientation: number }).orientation & 4
? env.image.height
: env.image.width;
originalHeight =
(env as unknown as { orientation: number }).orientation & 4
? env.image.width
: env.image.height;
return Promise.resolve(env);
});
const highestDimension = Math.max(originalWidth, originalHeight);
const placeholderDataURL = (
await Reducer.toCanvas(imageBlobOrFile, { max: 8 })
).toDataURL("image/png");
// Calculate the sizes to resize the image to
const resizes = [256, 1024, 2048, highestDimension]
.filter((s) => s <= (options?.maxSize ?? highestDimension))
.toSorted((a, b) => a - b);
// Get the highest resolution to use as final original size
// In case of options.maxSize, it's not the originalWidth/Height
const { width: finalWidth, height: finalHeight } = getNewDimensions(
originalWidth,
originalHeight,
resizes.at(-1)!,
);
const imageDefinition = ImageDefinition.create(
{
originalSize: [originalWidth, originalHeight],
placeholderDataURL,
},
{ originalSize: [finalWidth, finalHeight] },
options?.owner,
);
const owner = imageDefinition._owner;
const fillImageResolutions = async () => {
const max256 = await Reducer.toBlob(imageBlobOrFile, { max: 256 });
// Placeholder 8x8
imageDefinition.placeholderDataURL =
await getPlaceholderBase64(imageBlobOrFile);
if (originalWidth > 256 || originalHeight > 256) {
const width =
originalWidth > originalHeight
? 256
: Math.round(256 * (originalWidth / originalHeight));
const height =
originalHeight > originalWidth
? 256
: Math.round(256 * (originalHeight / originalWidth));
const binaryStream = await FileStream.createFromBlob(max256, owner);
imageDefinition[`${width}x${height}`] = binaryStream;
}
await new Promise((resolve) => setTimeout(resolve, 0));
if (options?.maxSize === 256) return;
const max1024 = await Reducer.toBlob(imageBlobOrFile, { max: 1024 });
if (originalWidth > 1024 || originalHeight > 1024) {
const width =
originalWidth > originalHeight
? 1024
: Math.round(1024 * (originalWidth / originalHeight));
const height =
originalHeight > originalWidth
? 1024
: Math.round(1024 * (originalHeight / originalWidth));
const binaryStream = await FileStream.createFromBlob(max1024, owner);
imageDefinition[`${width}x${height}`] = binaryStream;
}
await new Promise((resolve) => setTimeout(resolve, 0));
if (options?.maxSize === 1024) return;
const max2048 = await Reducer.toBlob(imageBlobOrFile, { max: 2048 });
if (originalWidth > 2048 || originalHeight > 2048) {
const width =
originalWidth > originalHeight
? 2048
: Math.round(2048 * (originalWidth / originalHeight));
const height =
originalHeight > originalWidth
? 2048
: Math.round(2048 * (originalHeight / originalWidth));
const binaryStream = await FileStream.createFromBlob(max2048, owner);
imageDefinition[`${width}x${height}`] = binaryStream;
}
await new Promise((resolve) => setTimeout(resolve, 0));
if (options?.maxSize === 2048) return;
const originalBinaryStream = await FileStream.createFromBlob(
imageBlobOrFile,
owner,
// Resizes for progressive loading
for (let size of resizes) {
// Calculate width and height respecting the aspect ratio
const { width, height } = getNewDimensions(
originalWidth,
originalHeight,
size,
);
imageDefinition[`${originalWidth}x${originalHeight}`] =
originalBinaryStream;
};
const image = await resize(imageBlobOrFile, width, height);
await fillImageResolutions();
const binaryStream = await FileStream.createFromBlob(image, owner);
imageDefinition[`${width}x${height}`] = binaryStream;
}
return imageDefinition;
}
async function getImageSize(
imageBlobOrFile: Blob | File,
): Promise<{ width: number; height: number }> {
const { width, height } = await new Promise<{
width: number;
height: number;
}>((resolve, reject) => {
const img = new Image();
img.onload = () => {
resolve({ width: img.width, height: img.height });
URL.revokeObjectURL(img.src);
};
img.onerror = () => {
reject(new Error("Failed to load image"));
URL.revokeObjectURL(img.src);
};
img.src = URL.createObjectURL(imageBlobOrFile);
});
return { width, height };
}
async function getPlaceholderBase64(
imageBlobOrFile: Blob | File,
): Promise<string> {
// Inizialize Reducer here to not have module side effects
if (!reducer) {
reducer = new ImageBlobReduce({ pica: new Pica() });
}
const canvas = await reducer.toCanvas(imageBlobOrFile, { max: 8 });
return canvas.toDataURL("image/png");
}
async function resize(
imageBlobOrFile: Blob | File,
width: number,
height: number,
): Promise<Blob> {
// Inizialize Reducer here to not have module side effects
if (!reducer) {
reducer = new ImageBlobReduce({ pica: new Pica() });
}
return reducer.toBlob(imageBlobOrFile, { max: Math.max(width, height) });
}
const getNewDimensions = (
originalWidth: number,
originalHeight: number,
maxSize: number,
) => {
const width =
originalWidth > originalHeight
? maxSize
: Math.round(maxSize * (originalWidth / originalHeight));
const height =
originalHeight > originalWidth
? maxSize
: Math.round(maxSize * (originalHeight / originalWidth));
return { width, height };
};

Some files were not shown because too many files have changed in this diff Show More