diff --git a/package-lock.json b/package-lock.json index 0b8dc90..9ed84a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,10 +9,13 @@ "version": "0.0.1", "dependencies": { "@zeabur/svelte-adapter": "^1.0.0", + "bits-ui": "^0.21.13", "canonicalize": "^2.0.0", "svelte-boring-avatars": "^1.2.6", + "svelte-hero-icons": "^5.2.0", "tailwind-merge": "^2.5.2", "typewriter-editor": "^0.12.6", + "unique-names-generator": "^4.7.1", "virtua": "^0.33.4", "zod": "^3.23.8" }, @@ -544,6 +547,31 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", + "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.7" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.10", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz", + "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.7" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz", + "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==", + "license": "MIT" + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -572,6 +600,15 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@internationalized/date": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.5.5.tgz", + "integrity": "sha512-H+CfYvOZ0LTJeeLOqm19E3uj/4YjrmOFtBufDHPfvtI80hFAMqtrp7oCACpe4Cil5l8S0Qu/9dYfZc/5lY8WQQ==", + "license": "Apache-2.0", + "dependencies": { + "@swc/helpers": "^0.5.0" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -701,6 +738,41 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@melt-ui/svelte": { + "version": "0.76.2", + "resolved": "https://registry.npmjs.org/@melt-ui/svelte/-/svelte-0.76.2.tgz", + "integrity": "sha512-7SbOa11tXUS95T3fReL+dwDs5FyJtCEqrqG3inRziDws346SYLsxOQ6HmX+4BkIsQh1R8U3XNa+EMmdMt38lMA==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.3.1", + "@floating-ui/dom": "^1.4.5", + "@internationalized/date": "^3.5.0", + "dequal": "^2.0.3", + "focus-trap": "^7.5.2", + "nanoid": "^5.0.4" + }, + "peerDependencies": { + "svelte": ">=3 <5" + } + }, + "node_modules/@melt-ui/svelte/node_modules/nanoid": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", + "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -991,6 +1063,12 @@ "win32" ] }, + "node_modules/@steeze-ui/heroicons": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@steeze-ui/heroicons/-/heroicons-2.4.0.tgz", + "integrity": "sha512-gxOytXYV/lQiGxkZQC+CP5IWCa+d/vscRKn4JfZXgF8MZEzg9wfl1tTi/GOr3Eg93YkhlhNDwTIGWjhMaEwmvw==", + "license": "MIT" + }, "node_modules/@sveltejs/adapter-auto": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/@sveltejs/adapter-auto/-/adapter-auto-3.2.4.tgz", @@ -1075,6 +1153,15 @@ "vite": "^5.0.0" } }, + "node_modules/@swc/helpers": { + "version": "0.5.12", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.12.tgz", + "integrity": "sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@tailwindcss/typography": { "version": "0.5.14", "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.14.tgz", @@ -2133,6 +2220,41 @@ "file-uri-to-path": "1.0.0" } }, + "node_modules/bits-ui": { + "version": "0.21.13", + "resolved": "https://registry.npmjs.org/bits-ui/-/bits-ui-0.21.13.tgz", + "integrity": "sha512-7nmOh6Ig7ND4DXZHv1FhNsY9yUGrad0+mf3tc4YN//3MgnJT1LnHtk4HZAKgmxCOe7txSX7/39LtYHbkrXokAQ==", + "license": "MIT", + "dependencies": { + "@internationalized/date": "^3.5.1", + "@melt-ui/svelte": "0.76.2", + "nanoid": "^5.0.5" + }, + "funding": { + "url": "https://github.com/sponsors/huntabyte" + }, + "peerDependencies": { + "svelte": "^4.0.0 || ^5.0.0-next.118" + } + }, + "node_modules/bits-ui/node_modules/nanoid": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.0.7.tgz", + "integrity": "sha512-oLxFY2gd2IqnjcYyOXD8XGCftpGtZP2AbHbOkthDkvRywH5ayNtPVy9YlOPcHckXzbLTCHpkb7FB+yuxKV13pQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^18 || >=20" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -3053,6 +3175,15 @@ "dev": true, "license": "ISC" }, + "node_modules/focus-trap": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/focus-trap/-/focus-trap-7.5.4.tgz", + "integrity": "sha512-N7kHdlgsO/v+iD/dMoJKtsSqs5Dz/dXZVebRgJw23LDk+jMi/974zyiOYDziY2JPp8xivq9BmUGwIJMiuSBi7w==", + "license": "MIT", + "dependencies": { + "tabbable": "^6.2.0" + } + }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", @@ -5263,6 +5394,21 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/svelte-hero-icons": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/svelte-hero-icons/-/svelte-hero-icons-5.2.0.tgz", + "integrity": "sha512-KpdMTL0bOnkxciEmDXvyVF/R5nrZ1x1uHCSt9gMrrbEd3g5HSIaaDChOutTOfeI+cZ3EJbb+OcBH/lBzJr1aEw==", + "license": "MIT", + "dependencies": { + "@steeze-ui/heroicons": "^2.4.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "svelte": "^3.0.0 || ^4.0.0 || ^5.0.0" + } + }, "node_modules/svelte-hmr": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.16.0.tgz", @@ -5338,6 +5484,12 @@ } } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" + }, "node_modules/tailwind-merge": { "version": "2.5.2", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.2.tgz", @@ -5620,6 +5772,12 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/tslib": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz", + "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==", + "license": "0BSD" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -5685,6 +5843,15 @@ "svelte": ">=3.43.0 <5" } }, + "node_modules/unique-names-generator": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/unique-names-generator/-/unique-names-generator-4.7.1.tgz", + "integrity": "sha512-lMx9dX+KRmG8sq6gulYYpKWZc9RlGsgBR6aoO8Qsm3qvkSJ+3rAymr+TnV8EDMrIrwuFJ4kruzMWM/OpYzPoow==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.0.tgz", diff --git a/package.json b/package.json index b1bf893..7323eb3 100644 --- a/package.json +++ b/package.json @@ -37,10 +37,13 @@ "type": "module", "dependencies": { "@zeabur/svelte-adapter": "^1.0.0", + "bits-ui": "^0.21.13", "canonicalize": "^2.0.0", "svelte-boring-avatars": "^1.2.6", + "svelte-hero-icons": "^5.2.0", "tailwind-merge": "^2.5.2", "typewriter-editor": "^0.12.6", + "unique-names-generator": "^4.7.1", "virtua": "^0.33.4", "zod": "^3.23.8" } diff --git a/src/app.css b/src/app.css index 233d770..bc5b027 100644 --- a/src/app.css +++ b/src/app.css @@ -4,6 +4,7 @@ @layer base { .weblah-light-theme { + --weblah-color-sb-overlay: theme(colors.white); --weblah-color-sb-primary: theme(colors.slate.50); --weblah-color-sb-secondary: theme(colors.slate.100); --weblah-color-sb-tertiary: theme(colors.slate.200); @@ -17,6 +18,7 @@ } .weblah-dark-theme { + --weblah-color-sb-overlay: theme(colors.slate.800); --weblah-color-sb-primary: theme(colors.slate.900); --weblah-color-sb-secondary: theme(colors.slate.950); --weblah-color-sb-tertiary: theme(colors.black); diff --git a/src/app.html b/src/app.html index d39fe3d..0d39f7d 100644 --- a/src/app.html +++ b/src/app.html @@ -11,7 +11,7 @@
%sveltekit.body%
diff --git a/src/lib/blah/crypto/keypair.ts b/src/lib/blah/crypto/keypair.ts index 9dcc27c..81df7e4 100644 --- a/src/lib/blah/crypto/keypair.ts +++ b/src/lib/blah/crypto/keypair.ts @@ -16,6 +16,9 @@ export class BlahKeyPair { get id() { return this.publicIdentity.id; } + get name() { + return this.publicIdentity.name; + } private constructor(publicIdentity: BlahPublicIdentity, privateKey: CryptoKey) { this.publicIdentity = publicIdentity; diff --git a/src/lib/blah/crypto/publicIdentity.ts b/src/lib/blah/crypto/publicIdentity.ts index 90e959b..166104d 100644 --- a/src/lib/blah/crypto/publicIdentity.ts +++ b/src/lib/blah/crypto/publicIdentity.ts @@ -1,14 +1,26 @@ import canonicalize from 'canonicalize'; import type { BlahSignedPayload } from './signedPayload'; import { bufToHex, hexToBuf } from './utils'; +import { adjectives, animals, uniqueNamesGenerator } from 'unique-names-generator'; + +export function generateName(id: string) { + return uniqueNamesGenerator({ + seed: id, + style: 'capital', + separator: ' ', + dictionaries: [adjectives, animals] + }); +} export class BlahPublicIdentity { private publicKey: CryptoKey; id: string; + name: string; private constructor(publicKey: CryptoKey, id: string) { this.publicKey = publicKey; this.id = id; + this.name = generateName(id); } static async fromPublicKey(publicKey: CryptoKey): Promise { diff --git a/src/lib/components/DropdownMenu.ts b/src/lib/components/DropdownMenu.ts new file mode 100644 index 0000000..458805f --- /dev/null +++ b/src/lib/components/DropdownMenu.ts @@ -0,0 +1,9 @@ +import { DropdownMenu } from 'bits-ui'; + +import Content from './DropdownMenu/Content.svelte'; +import Trigger from './DropdownMenu/Trigger.svelte'; +import Item from './DropdownMenu/Item.svelte'; + +const { Root, RadioGroup, RadioItem, Separator } = DropdownMenu; + +export { Root, Trigger, Content, Item, RadioGroup, RadioItem, Separator }; diff --git a/src/lib/components/DropdownMenu/Content.svelte b/src/lib/components/DropdownMenu/Content.svelte new file mode 100644 index 0000000..fdcaf5e --- /dev/null +++ b/src/lib/components/DropdownMenu/Content.svelte @@ -0,0 +1,13 @@ + + + + + diff --git a/src/lib/components/DropdownMenu/Item.svelte b/src/lib/components/DropdownMenu/Item.svelte new file mode 100644 index 0000000..24377f7 --- /dev/null +++ b/src/lib/components/DropdownMenu/Item.svelte @@ -0,0 +1,13 @@ + + + + + diff --git a/src/lib/components/DropdownMenu/Trigger.svelte b/src/lib/components/DropdownMenu/Trigger.svelte new file mode 100644 index 0000000..718d9c7 --- /dev/null +++ b/src/lib/components/DropdownMenu/Trigger.svelte @@ -0,0 +1,8 @@ + + + + + diff --git a/src/lib/keystore.ts b/src/lib/keystore.ts index 794dc10..334313a 100644 --- a/src/lib/keystore.ts +++ b/src/lib/keystore.ts @@ -2,3 +2,4 @@ import type { EncodedBlahKeyPair } from './blah/crypto'; import { localStore } from './localstore'; export const keyStore = localStore('weblah-keypairs', []); +export const currentKeyIndex = localStore('weblah-current-key-index', 0); diff --git a/src/routes/(app)/ChatListHeader.svelte b/src/routes/(app)/ChatListHeader.svelte index 9364e3b..882e41d 100644 --- a/src/routes/(app)/ChatListHeader.svelte +++ b/src/routes/(app)/ChatListHeader.svelte @@ -1,11 +1,11 @@
-
+ + import * as DropdownMenu from '$lib/components/DropdownMenu'; + import { AvatarBeam } from 'svelte-boring-avatars'; + import { keyStore, currentKeyIndex } from '$lib/keystore'; + import { BlahKeyPair, generateName } from '$lib/blah/crypto'; + + let currentKeyId: string | undefined; + $: currentKeyId = $keyStore[$currentKeyIndex]?.id; + $: currentKeyName = currentKeyId ? generateName(currentKeyId) : null; + + async function createKeyPair() { + const newKeyPair = await BlahKeyPair.generate(); + const encoded = await newKeyPair.encode(); + $keyStore = [...$keyStore, encoded]; + $currentKeyIndex = $keyStore.length - 1; + } + + + + + {#if currentKeyId} + + Using identity {currentKeyName} + {:else} +
+ Using no identity + {/if} + + + {#if $keyStore.length > 0} + + {#each $keyStore as { id }} + {@const name = generateName(id)} + + + {name} + + {/each} + + + {/if} + Create new identity + + diff --git a/tailwind.config.ts b/tailwind.config.ts index ab29def..fbd158f 100644 --- a/tailwind.config.ts +++ b/tailwind.config.ts @@ -18,6 +18,7 @@ export default { accent: colors.blue, // Semantic Background sb: { + overlay: 'var(--weblah-color-sb-overlay)', primary: 'var(--weblah-color-sb-primary)', secondary: 'var(--weblah-color-sb-secondary)', tertiary: 'var(--weblah-color-sb-tertiary)'