Compare commits
42 Commits
1.7.4
...
442829695b
| Author | SHA1 | Date | |
|---|---|---|---|
| 442829695b | |||
| 6267e52bdf | |||
| ab21008f34 | |||
| 0998639d59 | |||
| eccde07d06 | |||
| 770cdc5f13 | |||
| 6bafb62414 | |||
| 6ce0fbbbe6 | |||
| 8fe42e6f22 | |||
| 47a6209730 | |||
| 24d3f867f8 | |||
| 9db60374e4 | |||
| 8ef4b21dab | |||
| 8f56812dd1 | |||
| 3833cb093d | |||
| 94db65b85e | |||
| 6f731e48d2 | |||
| 99fe0e543c | |||
| c6b0799b2a | |||
| 861f2286db | |||
| 9af3e3b2e9 | |||
| 341c1b45b2 | |||
| 89f5d8cdf5 | |||
| ca3270437d | |||
| bbbc6f7363 | |||
| 8a0abacf6f | |||
| 976ccdabd4 | |||
| 99b26680b6 | |||
| c5be477855 | |||
| 32c1501e9c | |||
| 463837e7d4 | |||
| d74f142cdd | |||
| 53954aae89 | |||
| 24aa62a503 | |||
| 2618bb9c63 | |||
| 32a31045ef | |||
| 56edad77a8 | |||
| fdb604e350 | |||
| 3c66dfd83c | |||
| 81633b0a1e | |||
| 4a967de184 | |||
| 59961cbdb5 |
@@ -4,7 +4,6 @@ ios
|
|||||||
build
|
build
|
||||||
|
|
||||||
# Git
|
# Git
|
||||||
.git
|
|
||||||
.gitignore
|
.gitignore
|
||||||
|
|
||||||
# Env files (keep .env for build; exclude local overrides)
|
# Env files (keep .env for build; exclude local overrides)
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ on:
|
|||||||
branches: [master]
|
branches: [master]
|
||||||
|
|
||||||
env:
|
env:
|
||||||
REGISTRY: ghcr.io
|
REGISTRY: gitea.coracle.social
|
||||||
IMAGE_NAME: coracle-social/flotilla
|
IMAGE_NAME: coracle/flotilla
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-and-push-image:
|
build-and-push-image:
|
||||||
@@ -23,8 +23,8 @@ jobs:
|
|||||||
uses: docker/login-action@v3
|
uses: docker/login-action@v3
|
||||||
with:
|
with:
|
||||||
registry: ${{ env.REGISTRY }}
|
registry: ${{ env.REGISTRY }}
|
||||||
username: ${{ secrets.REGISTRY_USERNAME }}
|
username: hodlbod
|
||||||
password: ${{ secrets.REGISTRY_PASSWORD }}
|
password: ${{ secrets.PACKAGE_TOKEN }}
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels) for Docker
|
- name: Extract metadata (tags, labels) for Docker
|
||||||
id: meta
|
id: meta
|
||||||
|
|||||||
@@ -1,5 +1,31 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
# 1.8.0
|
||||||
|
|
||||||
|
* Fix relay badge overflow
|
||||||
|
* Suppress programmatic scroll when user is scrolling
|
||||||
|
* Fix vertical alignment of emoji and overflow buttons in shared event action row
|
||||||
|
* Use type=email for signup/login email inputs, validate password
|
||||||
|
* Improve toggle switch placement on settings screens
|
||||||
|
* Fix relay auth privacy toggle
|
||||||
|
* Improve field layout
|
||||||
|
* Add progress bar to signup flow
|
||||||
|
* Bundle emojis properly
|
||||||
|
* Rework hosting page
|
||||||
|
* Fix padding on pages on small screens
|
||||||
|
* Add richer link preview support
|
||||||
|
* Fix pasting into event summary
|
||||||
|
* Publish fewer join/claim requests
|
||||||
|
* Fix new messages not rendering in safari
|
||||||
|
* Avoid capturing stale cleanup function in chat
|
||||||
|
* Hide keyboard on app resume
|
||||||
|
* Add email rendering support
|
||||||
|
* Fix bunker login
|
||||||
|
* Fix undefined chat draft key
|
||||||
|
* Allow sharing to chat without a message
|
||||||
|
* Make sure to show date on calendar events when embedded
|
||||||
|
* Improve space search
|
||||||
|
|
||||||
# 1.7.4
|
# 1.7.4
|
||||||
|
|
||||||
* Fix safe area inset for FAB
|
* Fix safe area inset for FAB
|
||||||
|
|||||||
+15
-20
@@ -1,32 +1,27 @@
|
|||||||
# Stage 1: Build
|
# Build and run the Flotilla web server.
|
||||||
# Uses .env from build context for config (logo, branding, etc.)
|
#
|
||||||
# Optional: docker build --build-arg VITE_BUILD_HASH=$(git rev-parse --short HEAD) -t flotilla .
|
# docker build -t flotilla .
|
||||||
|
# docker run -p 3000:3000 flotilla
|
||||||
|
#
|
||||||
|
# Pass --build-arg VITE_BUILD_HASH=$(git rev-parse --short HEAD) to stamp the build.
|
||||||
|
# A .env in the build context is picked up by build.sh for branding config.
|
||||||
|
|
||||||
FROM node:20-bookworm AS builder
|
FROM node:22-bookworm
|
||||||
|
|
||||||
RUN apt-get update && apt-get install -y --no-install-recommends curl
|
RUN npm install -g pnpm@10.33.0
|
||||||
|
|
||||||
RUN npm install -g pnpm@latest
|
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY package.json pnpm-lock.yaml ./
|
COPY package.json pnpm-lock.yaml ./
|
||||||
RUN pnpm i
|
|
||||||
|
|
||||||
# Copy everything (including .env when present) - build.sh will source it
|
RUN pnpm i --frozen-lockfile
|
||||||
COPY . .
|
|
||||||
|
|
||||||
ARG VITE_BUILD_HASH
|
|
||||||
ENV VITE_BUILD_HASH=${VITE_BUILD_HASH}
|
|
||||||
|
|
||||||
ENV NODE_OPTIONS=--max_old_space_size=16384
|
ENV NODE_OPTIONS=--max_old_space_size=16384
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
RUN pnpm run build
|
RUN pnpm run build
|
||||||
|
|
||||||
FROM node:20-alpine
|
EXPOSE 3000
|
||||||
|
|
||||||
WORKDIR /app
|
CMD ["node", "server.js"]
|
||||||
|
|
||||||
# Copy only the built output - no source, no .env, no dev deps
|
|
||||||
COPY --from=builder /app/build ./build
|
|
||||||
|
|
||||||
CMD ["npx", "serve", "-s", "build"]
|
|
||||||
|
|||||||
@@ -31,18 +31,18 @@ To run your own Flotilla, it's as simple as:
|
|||||||
```sh
|
```sh
|
||||||
pnpm install
|
pnpm install
|
||||||
pnpm run build
|
pnpm run build
|
||||||
npx serve -s build
|
pnpm run start
|
||||||
```
|
```
|
||||||
|
|
||||||
Or, if you prefer to use a container:
|
Or, if you prefer to use a container:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
podman run -d -p 3000:3000 ghcr.io/coracle-social/flotilla:latest
|
docker run -d -p 3000:3000 gitea.coracle.social/coracle/flotilla:latest
|
||||||
```
|
```
|
||||||
|
|
||||||
Alternatively, you can copy the build files into a directory of your choice and serve it yourself:
|
Alternatively, you can copy the build files into a directory of your choice and serve it yourself:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
mkdir ./mount
|
mkdir ./mount
|
||||||
podman run -v ./mount:/app/mount ghcr.io/coracle-social/flotilla:latest bash -c 'cp -r build/* mount'
|
docker run -v ./mount:/app/mount gitea.coracle.social/coracle/flotilla:latest bash -c 'cp -r build/* mount'
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -8,8 +8,8 @@ android {
|
|||||||
applicationId "social.flotilla"
|
applicationId "social.flotilla"
|
||||||
minSdk rootProject.ext.minSdkVersion
|
minSdk rootProject.ext.minSdkVersion
|
||||||
targetSdk rootProject.ext.targetSdkVersion
|
targetSdk rootProject.ext.targetSdkVersion
|
||||||
versionCode 46
|
versionCode 47
|
||||||
versionName "1.7.4"
|
versionName "1.8.0"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
aaptOptions {
|
aaptOptions {
|
||||||
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
archiveVersion = 1;
|
archiveVersion = 1;
|
||||||
classes = {
|
classes = {
|
||||||
};
|
};
|
||||||
objectVersion = 48;
|
objectVersion = 54;
|
||||||
objects = {
|
objects = {
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
@@ -131,8 +131,9 @@
|
|||||||
504EC2FC1FED79650016851F /* Project object */ = {
|
504EC2FC1FED79650016851F /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
|
BuildIndependentTargetsInParallel = YES;
|
||||||
LastSwiftUpdateCheck = 920;
|
LastSwiftUpdateCheck = 920;
|
||||||
LastUpgradeCheck = 920;
|
LastUpgradeCheck = 2630;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
504EC3031FED79650016851F = {
|
504EC3031FED79650016851F = {
|
||||||
CreatedOnToolsVersion = 9.2;
|
CreatedOnToolsVersion = 9.2;
|
||||||
@@ -257,6 +258,7 @@
|
|||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
CLANG_WARN_COMMA = YES;
|
CLANG_WARN_COMMA = YES;
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
@@ -264,8 +266,10 @@
|
|||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -275,8 +279,10 @@
|
|||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
DEBUG_INFORMATION_FORMAT = dwarf;
|
||||||
|
DEVELOPMENT_TEAM = S26U9DYW3A;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
ENABLE_TESTABILITY = YES;
|
ENABLE_TESTABILITY = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
GCC_DYNAMIC_NO_PIC = NO;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
@@ -295,6 +301,7 @@
|
|||||||
MTL_ENABLE_DEBUG_INFO = YES;
|
MTL_ENABLE_DEBUG_INFO = YES;
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
};
|
};
|
||||||
@@ -314,6 +321,7 @@
|
|||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
CLANG_WARN_BOOL_CONVERSION = YES;
|
||||||
CLANG_WARN_COMMA = YES;
|
CLANG_WARN_COMMA = YES;
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
||||||
|
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
CLANG_WARN_EMPTY_BODY = YES;
|
||||||
@@ -321,8 +329,10 @@
|
|||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
CLANG_WARN_INFINITE_RECURSION = YES;
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
CLANG_WARN_INT_CONVERSION = YES;
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
||||||
|
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||||
|
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||||
@@ -332,8 +342,10 @@
|
|||||||
CODE_SIGN_IDENTITY = "iPhone Developer";
|
CODE_SIGN_IDENTITY = "iPhone Developer";
|
||||||
COPY_PHASE_STRIP = NO;
|
COPY_PHASE_STRIP = NO;
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
||||||
|
DEVELOPMENT_TEAM = S26U9DYW3A;
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
ENABLE_NS_ASSERTIONS = NO;
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
||||||
|
ENABLE_USER_SCRIPT_SANDBOXING = NO;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
GCC_C_LANGUAGE_STANDARD = gnu11;
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
GCC_NO_COMMON_BLOCKS = YES;
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
||||||
@@ -345,7 +357,9 @@
|
|||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
|
STRING_CATALOG_GENERATE_SYMBOLS = YES;
|
||||||
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
VALIDATE_PRODUCT = YES;
|
VALIDATE_PRODUCT = YES;
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
@@ -358,14 +372,16 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 37;
|
CURRENT_PROJECT_VERSION = 38;
|
||||||
DEVELOPMENT_TEAM = S26U9DYW3A;
|
|
||||||
INFOPLIST_FILE = App/Info.plist;
|
INFOPLIST_FILE = App/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
|
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
MARKETING_VERSION = 1.7.4;
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.8.0;
|
||||||
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
@@ -385,14 +401,16 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "Flotilla Chat.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 37;
|
CURRENT_PROJECT_VERSION = 38;
|
||||||
DEVELOPMENT_TEAM = S26U9DYW3A;
|
|
||||||
INFOPLIST_FILE = App/Info.plist;
|
INFOPLIST_FILE = App/Info.plist;
|
||||||
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
|
INFOPLIST_KEY_CFBundleDisplayName = "Flotilla Chat";
|
||||||
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking";
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = (
|
||||||
MARKETING_VERSION = 1.7.4;
|
"$(inherited)",
|
||||||
|
"@executable_path/Frameworks",
|
||||||
|
);
|
||||||
|
MARKETING_VERSION = 1.8.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
PRODUCT_BUNDLE_IDENTIFIER = social.flotilla;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
|||||||
+16
-11
@@ -1,10 +1,11 @@
|
|||||||
{
|
{
|
||||||
"name": "flotilla",
|
"name": "flotilla",
|
||||||
"version": "1.7.4",
|
"version": "1.8.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite dev",
|
||||||
"build": "./build.sh",
|
"build": "./build.sh",
|
||||||
|
"start": "node server.js",
|
||||||
"release:android": "./build.sh && cap build android --androidreleasetype APK --signing-type apksigner",
|
"release:android": "./build.sh && cap build android --androidreleasetype APK --signing-type apksigner",
|
||||||
"tauri:dev": "tauri dev",
|
"tauri:dev": "tauri dev",
|
||||||
"tauri:build": "tauri build",
|
"tauri:build": "tauri build",
|
||||||
@@ -60,6 +61,7 @@
|
|||||||
"@capawesome/capacitor-badge": "^8.0.0",
|
"@capawesome/capacitor-badge": "^8.0.0",
|
||||||
"@getalby/lightning-tools": "^6.1.0",
|
"@getalby/lightning-tools": "^6.1.0",
|
||||||
"@getalby/sdk": "^5.1.2",
|
"@getalby/sdk": "^5.1.2",
|
||||||
|
"@hono/node-server": "^2.0.0",
|
||||||
"@noble/curves": "^1.9.7",
|
"@noble/curves": "^1.9.7",
|
||||||
"@pomade/core": "^0.2.3",
|
"@pomade/core": "^0.2.3",
|
||||||
"@poppanator/sveltekit-svg": "^4.2.1",
|
"@poppanator/sveltekit-svg": "^4.2.1",
|
||||||
@@ -70,22 +72,25 @@
|
|||||||
"@types/throttle-debounce": "^5.0.2",
|
"@types/throttle-debounce": "^5.0.2",
|
||||||
"@vite-pwa/assets-generator": "^0.2.6",
|
"@vite-pwa/assets-generator": "^0.2.6",
|
||||||
"@vite-pwa/sveltekit": "^0.6.8",
|
"@vite-pwa/sveltekit": "^0.6.8",
|
||||||
"@welshman/app": "^0.8.13",
|
"@welshman/app": "^0.8.15",
|
||||||
"@welshman/content": "^0.8.13",
|
"@welshman/content": "^0.8.15",
|
||||||
"@welshman/editor": "^0.8.13",
|
"@welshman/editor": "^0.8.15",
|
||||||
"@welshman/feeds": "^0.8.13",
|
"@welshman/feeds": "^0.8.15",
|
||||||
"@welshman/lib": "^0.8.13",
|
"@welshman/lib": "^0.8.15",
|
||||||
"@welshman/net": "^0.8.13",
|
"@welshman/net": "^0.8.15",
|
||||||
"@welshman/router": "^0.8.13",
|
"@welshman/router": "^0.8.15",
|
||||||
"@welshman/signer": "^0.8.13",
|
"@welshman/signer": "^0.8.15",
|
||||||
"@welshman/store": "^0.8.13",
|
"@welshman/store": "^0.8.15",
|
||||||
"@welshman/util": "^0.8.13",
|
"@welshman/util": "^0.8.15",
|
||||||
|
"cheerio": "^1.2.0",
|
||||||
"compressorjs-next": "^1.1.2",
|
"compressorjs-next": "^1.1.2",
|
||||||
"daisyui": "^5.5.19",
|
"daisyui": "^5.5.19",
|
||||||
"date-picker-svelte": "^2.17.0",
|
"date-picker-svelte": "^2.17.0",
|
||||||
"dotenv": "^16.6.1",
|
"dotenv": "^16.6.1",
|
||||||
"emoji-picker-element": "^1.28.1",
|
"emoji-picker-element": "^1.28.1",
|
||||||
|
"emoji-picker-element-data": "^1.8.0",
|
||||||
"fuse.js": "^7.1.0",
|
"fuse.js": "^7.1.0",
|
||||||
|
"hono": "^4.12.15",
|
||||||
"husky": "^9.1.7",
|
"husky": "^9.1.7",
|
||||||
"idb": "^8.0.3",
|
"idb": "^8.0.3",
|
||||||
"livekit-client": "^2.17.2",
|
"livekit-client": "^2.17.2",
|
||||||
|
|||||||
Generated
+259
-110
@@ -62,12 +62,15 @@ importers:
|
|||||||
'@getalby/sdk':
|
'@getalby/sdk':
|
||||||
specifier: ^5.1.2
|
specifier: ^5.1.2
|
||||||
version: 5.1.2(typescript@5.9.3)
|
version: 5.1.2(typescript@5.9.3)
|
||||||
|
'@hono/node-server':
|
||||||
|
specifier: ^2.0.0
|
||||||
|
version: 2.0.0(hono@4.12.15)
|
||||||
'@noble/curves':
|
'@noble/curves':
|
||||||
specifier: ^1.9.7
|
specifier: ^1.9.7
|
||||||
version: 1.9.7
|
version: 1.9.7
|
||||||
'@pomade/core':
|
'@pomade/core':
|
||||||
specifier: ^0.2.3
|
specifier: ^0.2.3
|
||||||
version: 0.2.3(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.13(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))
|
version: 0.2.3(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.15(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
'@poppanator/sveltekit-svg':
|
'@poppanator/sveltekit-svg':
|
||||||
specifier: ^4.2.1
|
specifier: ^4.2.1
|
||||||
version: 4.2.1(rollup@2.80.0)(svelte@5.48.0)(svgo@3.3.2)(vite@5.4.21(@types/node@25.0.10)(lightningcss@1.32.0)(terser@5.46.0))
|
version: 4.2.1(rollup@2.80.0)(svelte@5.48.0)(svgo@3.3.2)(vite@5.4.21(@types/node@25.0.10)(lightningcss@1.32.0)(terser@5.46.0))
|
||||||
@@ -93,35 +96,38 @@ importers:
|
|||||||
specifier: ^0.6.8
|
specifier: ^0.6.8
|
||||||
version: 0.6.8(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.48.0)(vite@5.4.21(@types/node@25.0.10)(lightningcss@1.32.0)(terser@5.46.0)))(svelte@5.48.0)(typescript@5.9.3)(vite@5.4.21(@types/node@25.0.10)(lightningcss@1.32.0)(terser@5.46.0)))(@vite-pwa/assets-generator@0.2.6)(vite-plugin-pwa@0.21.2(@vite-pwa/assets-generator@0.2.6)(vite@5.4.21(@types/node@25.0.10)(lightningcss@1.32.0)(terser@5.46.0))(workbox-build@7.3.0)(workbox-window@7.3.0))
|
version: 0.6.8(@sveltejs/kit@2.50.1(@sveltejs/vite-plugin-svelte@4.0.4(svelte@5.48.0)(vite@5.4.21(@types/node@25.0.10)(lightningcss@1.32.0)(terser@5.46.0)))(svelte@5.48.0)(typescript@5.9.3)(vite@5.4.21(@types/node@25.0.10)(lightningcss@1.32.0)(terser@5.46.0)))(@vite-pwa/assets-generator@0.2.6)(vite-plugin-pwa@0.21.2(@vite-pwa/assets-generator@0.2.6)(vite@5.4.21(@types/node@25.0.10)(lightningcss@1.32.0)(terser@5.46.0))(workbox-build@7.3.0)(workbox-window@7.3.0))
|
||||||
'@welshman/app':
|
'@welshman/app':
|
||||||
specifier: ^0.8.13
|
specifier: ^0.8.15
|
||||||
version: 0.8.13(ed9ee8a79a580bcb9fa9bb6eb1a69558)
|
version: 0.8.15(ff026297546a8274624eb18a0ea86191)
|
||||||
'@welshman/content':
|
'@welshman/content':
|
||||||
specifier: ^0.8.13
|
specifier: ^0.8.15
|
||||||
version: 0.8.13(nostr-tools@2.20.0(typescript@5.9.3))
|
version: 0.8.15(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
'@welshman/editor':
|
'@welshman/editor':
|
||||||
specifier: ^0.8.13
|
specifier: ^0.8.15
|
||||||
version: 0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-editor@1.1.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/extension-image@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)))(@tiptap/extension-link@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)(linkifyjs@4.3.2)(nostr-tools@2.20.0(typescript@5.9.3))(prosemirror-markdown@1.13.3)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.5)(tiptap-markdown@0.8.10(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))))(nostr-tools@2.20.0(typescript@5.9.3))
|
version: 0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-editor@1.1.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/extension-image@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)))(@tiptap/extension-link@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)(linkifyjs@4.3.2)(nostr-tools@2.20.0(typescript@5.9.3))(prosemirror-markdown@1.13.3)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.5)(tiptap-markdown@0.8.10(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))))(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
'@welshman/feeds':
|
'@welshman/feeds':
|
||||||
specifier: ^0.8.13
|
specifier: ^0.8.15
|
||||||
version: 0.8.13(29451a19e278ea4a9cf66616f05d5557)
|
version: 0.8.15(6e55dcd4e7516745e7b0228620d35545)
|
||||||
'@welshman/lib':
|
'@welshman/lib':
|
||||||
specifier: ^0.8.13
|
specifier: ^0.8.15
|
||||||
version: 0.8.13
|
version: 0.8.15
|
||||||
'@welshman/net':
|
'@welshman/net':
|
||||||
specifier: ^0.8.13
|
specifier: ^0.8.15
|
||||||
version: 0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
version: 0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
||||||
'@welshman/router':
|
'@welshman/router':
|
||||||
specifier: ^0.8.13
|
specifier: ^0.8.15
|
||||||
version: 0.8.13(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))
|
version: 0.8.15(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))
|
||||||
'@welshman/signer':
|
'@welshman/signer':
|
||||||
specifier: ^0.8.13
|
specifier: ^0.8.15
|
||||||
version: 0.8.13(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3))
|
version: 0.8.15(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
'@welshman/store':
|
'@welshman/store':
|
||||||
specifier: ^0.8.13
|
specifier: ^0.8.15
|
||||||
version: 0.8.13(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(svelte@5.48.0)
|
version: 0.8.15(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(svelte@5.48.0)
|
||||||
'@welshman/util':
|
'@welshman/util':
|
||||||
specifier: ^0.8.13
|
specifier: ^0.8.15
|
||||||
version: 0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3))
|
version: 0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
|
cheerio:
|
||||||
|
specifier: ^1.2.0
|
||||||
|
version: 1.2.0
|
||||||
compressorjs-next:
|
compressorjs-next:
|
||||||
specifier: ^1.1.2
|
specifier: ^1.1.2
|
||||||
version: 1.1.2
|
version: 1.1.2
|
||||||
@@ -137,9 +143,15 @@ importers:
|
|||||||
emoji-picker-element:
|
emoji-picker-element:
|
||||||
specifier: ^1.28.1
|
specifier: ^1.28.1
|
||||||
version: 1.28.1
|
version: 1.28.1
|
||||||
|
emoji-picker-element-data:
|
||||||
|
specifier: ^1.8.0
|
||||||
|
version: 1.8.0
|
||||||
fuse.js:
|
fuse.js:
|
||||||
specifier: ^7.1.0
|
specifier: ^7.1.0
|
||||||
version: 7.1.0
|
version: 7.1.0
|
||||||
|
hono:
|
||||||
|
specifier: ^4.12.15
|
||||||
|
version: 4.12.15
|
||||||
husky:
|
husky:
|
||||||
specifier: ^9.1.7
|
specifier: ^9.1.7
|
||||||
version: 9.1.7
|
version: 9.1.7
|
||||||
@@ -1096,6 +1108,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-yUF9LhuvdIFOwjV1aG0ryzfwDiGBFk/CRLkRvrrM9dsE38SUjKsf1FDga5jxsKMu80nWcPZR9TiGGASWedoYPA==}
|
resolution: {integrity: sha512-yUF9LhuvdIFOwjV1aG0ryzfwDiGBFk/CRLkRvrrM9dsE38SUjKsf1FDga5jxsKMu80nWcPZR9TiGGASWedoYPA==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
|
||||||
|
'@hono/node-server@2.0.0':
|
||||||
|
resolution: {integrity: sha512-n3GfHwwCvHCkGmOwKfxUPOlbfzuO64Sbc5XC4NGPIXxkuOnJrdgExdRKmHfF924r914WRJPT397GdqLvdYTeyQ==}
|
||||||
|
engines: {node: '>=20'}
|
||||||
|
peerDependencies:
|
||||||
|
hono: ^4
|
||||||
|
|
||||||
'@humanfs/core@0.19.1':
|
'@humanfs/core@0.19.1':
|
||||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||||
engines: {node: '>=18.18.0'}
|
engines: {node: '>=18.18.0'}
|
||||||
@@ -2150,83 +2168,83 @@ packages:
|
|||||||
'@vite-pwa/assets-generator':
|
'@vite-pwa/assets-generator':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@welshman/app@0.8.13':
|
'@welshman/app@0.8.15':
|
||||||
resolution: {integrity: sha512-+mUMtt5ibotBk/susPFKXnb9jRjqvIQgWMF28poCIzse08V4kfVClJJlzepvgjqRn6Ma/takr6tNkL6eV4rlRQ==}
|
resolution: {integrity: sha512-GDo6w+UI/ldnh47c5IEDYWw8nbiyhnH4abJNy/q/jLBUwJ9SuiJ7GVVvhZ+t4XEo5NEMq+y4OLZs08+abf85MQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@pomade/core': ^0.2.1
|
'@pomade/core': ^0.2.1
|
||||||
'@welshman/feeds': 0.8.13
|
'@welshman/feeds': 0.8.15
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13
|
'@welshman/net': 0.8.15
|
||||||
'@welshman/router': 0.8.13
|
'@welshman/router': 0.8.15
|
||||||
'@welshman/signer': 0.8.13
|
'@welshman/signer': 0.8.15
|
||||||
'@welshman/store': 0.8.13
|
'@welshman/store': 0.8.15
|
||||||
'@welshman/util': 0.8.13
|
'@welshman/util': 0.8.15
|
||||||
svelte: ^4.0.0 || ^5.0.0
|
svelte: ^4.0.0 || ^5.0.0
|
||||||
|
|
||||||
'@welshman/content@0.8.13':
|
'@welshman/content@0.8.15':
|
||||||
resolution: {integrity: sha512-6ZDKCJ2GKczAULD7P7NZ5DmxFYKw6vfxJ1jpwbQj+0l7alr2IBh8kmaQ8wM1r6n0qOhfcNqeGaaREQxC4VnuHA==}
|
resolution: {integrity: sha512-5qe+6Es1r62HkVdeHJPsWkOpLjhdxBTtw3d4+Or1JXl8BgpUE2JV7e+5HQQqnPRVHt3nt14YPt0oirar5p1Fvg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
nostr-tools: ^2.19.4
|
nostr-tools: ^2.19.4
|
||||||
|
|
||||||
'@welshman/editor@0.8.13':
|
'@welshman/editor@0.8.15':
|
||||||
resolution: {integrity: sha512-kr4pSjQ/TZnlyIeGo0UNNAQrTGpp0yMRUFD/LwORVLnC8UGNLwGRmFwOz0WNtCxGxFGquTlX1AkNfViWdkfXHw==}
|
resolution: {integrity: sha512-lqTLQGf54yPioBn2KQsF7F5ExWM6Co31wgGaUAhCSeUGiTzUQgMEut4/N8VB1rFZ0wqU6zyPG5jgeuhFhRJWSw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/util': 0.8.13
|
'@welshman/util': 0.8.15
|
||||||
nostr-editor: ^1.1.1
|
nostr-editor: ^1.1.1
|
||||||
nostr-tools: ^2.19.4
|
nostr-tools: ^2.19.4
|
||||||
|
|
||||||
'@welshman/feeds@0.8.13':
|
'@welshman/feeds@0.8.15':
|
||||||
resolution: {integrity: sha512-zjjKbGG8wQyyuTtm7/7lAGEFbreTp7IO5Y+DZXwBBu/h2/TP/C/v0J0XrshFBqs/wOOURv7vYZlf/bs2En8UIg==}
|
resolution: {integrity: sha512-xIQDKdV6uLxOz5qJUbc/2HC6qnikgH1GPoHQwBpwKH7Lga6a7IGLOR6kvghUaPpulKcuF4MxG9gmvEHqgsQkJw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13
|
'@welshman/net': 0.8.15
|
||||||
'@welshman/router': 0.8.13
|
'@welshman/router': 0.8.15
|
||||||
'@welshman/signer': 0.8.13
|
'@welshman/signer': 0.8.15
|
||||||
'@welshman/util': 0.8.13
|
'@welshman/util': 0.8.15
|
||||||
|
|
||||||
'@welshman/lib@0.8.13':
|
'@welshman/lib@0.8.15':
|
||||||
resolution: {integrity: sha512-fXVoe7zx+jPnqZdRMXLNOJvW+N6E708HSpNGfyBGlu1/OXg70wkEK3r9E67HsBg7pLxnl22tcOYq7r11GhpeFA==}
|
resolution: {integrity: sha512-d7o6WUSVYXOstpWTqOBDfkSyr3GOBm/UMbgFx3RXCxzib0cWm7z0w1oLWvy1N7fjHc/Jp65G2KRpT6//B9yAww==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
|
|
||||||
'@welshman/net@0.8.13':
|
'@welshman/net@0.8.15':
|
||||||
resolution: {integrity: sha512-k9BQA2lJI1mnQrf3pR8e3QhCluPtWSSPz2ywTDKq+/pdVXXIjrnsblHA/62d6SjCCSV/n5fONQ08YMivPzgtGA==}
|
resolution: {integrity: sha512-AeJ/Vy7T6ruf1mjzzEUdH+aX5JriQKBzRn1zWZ4l8VEgxwc4w2bVte9a6aPnNJWc7JZT8ws8z+wOi4ECb6NPNA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/util': 0.8.13
|
'@welshman/util': 0.8.15
|
||||||
|
|
||||||
'@welshman/router@0.8.13':
|
'@welshman/router@0.8.15':
|
||||||
resolution: {integrity: sha512-MJh8YfHpoSsRUI96OnqxnBDoQwjqIMh8N57US0id9cd6iOlkYlVPEUeicJK8Kcl5oT0zmN13UT/4o3d7nZrqcA==}
|
resolution: {integrity: sha512-3lxcCYMaPX0gFaoM1GjBRvXr4UrnPA3o/mBII2Zm3gJeFuXN3XG+REwIN6QNhvTB7syTCTwx+dRdHgvqHl9N6g==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13
|
'@welshman/net': 0.8.15
|
||||||
'@welshman/util': 0.8.13
|
'@welshman/util': 0.8.15
|
||||||
|
|
||||||
'@welshman/signer@0.8.13':
|
'@welshman/signer@0.8.15':
|
||||||
resolution: {integrity: sha512-VgyKxZhJ/Br0q4H8KPfRWAa8WC0EVUc69dxq/Bt1cl7MTBg1EbzolUJhgOgGDOVO0gAKmWYMCnjNochaQy/Wpg==}
|
resolution: {integrity: sha512-Y96XZtsCHz8h7NK28sSi3CX+8lGG6WhLyVNyhlEhfypAxxx8Zpfr4GlSPApvp4tvm1//YfDtXHIIZTPXbmnqvA==}
|
||||||
version: 0.8.13
|
version: 0.8.15
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@noble/curves': ^1.9.7
|
'@noble/curves': ^1.9.7
|
||||||
'@noble/hashes': ^2.0.1
|
'@noble/hashes': ^2.0.1
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13
|
'@welshman/net': 0.8.15
|
||||||
'@welshman/util': 0.8.13
|
'@welshman/util': 0.8.15
|
||||||
nostr-signer-capacitor-plugin: '*'
|
nostr-signer-capacitor-plugin: '*'
|
||||||
nostr-tools: ^2.19.4
|
nostr-tools: ^2.19.4
|
||||||
|
|
||||||
'@welshman/store@0.8.13':
|
'@welshman/store@0.8.15':
|
||||||
resolution: {integrity: sha512-tnmbaNa8aqFVbklsMZ5y4h9xlHnbwo7o1l6xxJI0hqZnTuXD3IvN5/V58qhfZveUN/Y5Gz2MWQHFWyRBQ71ANg==}
|
resolution: {integrity: sha512-3rQVhAsQ1z5tcUzkJPkzVp3iBkMrUKVoBi07AYefqlhRoddhwB2pDBVhdZYoP2kl9wVPZlPV58vlD6BTo6TEwA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13
|
'@welshman/net': 0.8.15
|
||||||
'@welshman/util': 0.8.13
|
'@welshman/util': 0.8.15
|
||||||
svelte: ^4.0.0 || ^5.0.0
|
svelte: ^4.0.0 || ^5.0.0
|
||||||
|
|
||||||
'@welshman/util@0.8.13':
|
'@welshman/util@0.8.15':
|
||||||
resolution: {integrity: sha512-3+CNqJjiHGXKzLOniDqAN4Oe038fV1RRjKiVP0++FDVbq8lShtdcliR7FDg/NTjhhmzivhYqdflNvqjAqOxekA==}
|
resolution: {integrity: sha512-zeNWMyOtIpOqj9/hBAT8qWvnp5w/IyrcT7CmDKLkWt6NU6ZoZ3pF5duTwtOYZqcftYJaHXgohOt0RsHVPR3M7w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@noble/curves': ^1.9.7
|
'@noble/curves': ^1.9.7
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
nostr-tools: ^2.19.4
|
nostr-tools: ^2.19.4
|
||||||
|
|
||||||
'@xml-tools/parser@1.0.11':
|
'@xml-tools/parser@1.0.11':
|
||||||
@@ -2465,6 +2483,13 @@ packages:
|
|||||||
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
cheerio-select@2.1.0:
|
||||||
|
resolution: {integrity: sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==}
|
||||||
|
|
||||||
|
cheerio@1.2.0:
|
||||||
|
resolution: {integrity: sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==}
|
||||||
|
engines: {node: '>=20.18.1'}
|
||||||
|
|
||||||
chevrotain@7.1.1:
|
chevrotain@7.1.1:
|
||||||
resolution: {integrity: sha512-wy3mC1x4ye+O+QkEinVJkPf5u2vsrDIYW9G7ZuwFl6v/Yu0LwUuT2POsb+NUWApebyxfkQq6+yDfRExbnI5rcw==}
|
resolution: {integrity: sha512-wy3mC1x4ye+O+QkEinVJkPf5u2vsrDIYW9G7ZuwFl6v/Yu0LwUuT2POsb+NUWApebyxfkQq6+yDfRExbnI5rcw==}
|
||||||
|
|
||||||
@@ -2824,12 +2849,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==}
|
resolution: {integrity: sha512-wkgGT6kugeQk/P6VZ/f4T+4HB41BVgNBq5CDIZVbQ02nvTVqAiVTbskxxu3eA/X96lMlfYOwnLQpN2v5E1zDEg==}
|
||||||
engines: {node: '>= 0.4.0'}
|
engines: {node: '>= 0.4.0'}
|
||||||
|
|
||||||
|
emoji-picker-element-data@1.8.0:
|
||||||
|
resolution: {integrity: sha512-VfRuRJNEDLS1JKlNS4olaqhjX5S1nnZ+ZHG73b/dV8QeZyi0yPruTPEE72EmF6XO3k/9hj3lybMIYMOYXb/57A==}
|
||||||
|
|
||||||
emoji-picker-element@1.28.1:
|
emoji-picker-element@1.28.1:
|
||||||
resolution: {integrity: sha512-8c64IPish2PWoV9oYCo2pvuPHwIv+uK9bO0dfpPyMupDAvaWL9ZvYhWNTAR+2sx7BhfRjciImqP6CIUgNX+DMg==}
|
resolution: {integrity: sha512-8c64IPish2PWoV9oYCo2pvuPHwIv+uK9bO0dfpPyMupDAvaWL9ZvYhWNTAR+2sx7BhfRjciImqP6CIUgNX+DMg==}
|
||||||
|
|
||||||
emoji-regex@8.0.0:
|
emoji-regex@8.0.0:
|
||||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
|
encoding-sniffer@0.2.1:
|
||||||
|
resolution: {integrity: sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==}
|
||||||
|
|
||||||
enhanced-resolve@5.20.1:
|
enhanced-resolve@5.20.1:
|
||||||
resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==}
|
resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==}
|
||||||
engines: {node: '>=10.13.0'}
|
engines: {node: '>=10.13.0'}
|
||||||
@@ -2841,6 +2872,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
|
||||||
engines: {node: '>=0.12'}
|
engines: {node: '>=0.12'}
|
||||||
|
|
||||||
|
entities@6.0.1:
|
||||||
|
resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
|
||||||
|
engines: {node: '>=0.12'}
|
||||||
|
|
||||||
|
entities@7.0.1:
|
||||||
|
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
|
||||||
|
engines: {node: '>=0.12'}
|
||||||
|
|
||||||
env-paths@2.2.1:
|
env-paths@2.2.1:
|
||||||
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
|
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@@ -3234,6 +3273,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
hono@4.12.15:
|
||||||
|
resolution: {integrity: sha512-qM0jDhFEaCBb4TxoW7f53Qrpv9RBiayUHo0S52JudprkhvpjIrGoU1mnnr29Fvd1U335ZFPZQY1wlkqgfGXyLg==}
|
||||||
|
engines: {node: '>=16.9.0'}
|
||||||
|
|
||||||
hosted-git-info@2.8.9:
|
hosted-git-info@2.8.9:
|
||||||
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
|
resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==}
|
||||||
|
|
||||||
@@ -3241,6 +3284,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==}
|
resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
|
htmlparser2@10.1.0:
|
||||||
|
resolution: {integrity: sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==}
|
||||||
|
|
||||||
husky@9.1.7:
|
husky@9.1.7:
|
||||||
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
|
resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -3249,6 +3295,10 @@ packages:
|
|||||||
ico-endec@0.1.6:
|
ico-endec@0.1.6:
|
||||||
resolution: {integrity: sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ==}
|
resolution: {integrity: sha512-ZdLU38ZoED3g1j3iEyzcQj+wAkY2xfWNkymszfJPoxucIUhK7NayQ+/C4Kv0nDFMIsbtbEHldv3V8PU494/ueQ==}
|
||||||
|
|
||||||
|
iconv-lite@0.6.3:
|
||||||
|
resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
|
||||||
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
idb@7.1.1:
|
idb@7.1.1:
|
||||||
resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
|
resolution: {integrity: sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ==}
|
||||||
|
|
||||||
@@ -4015,6 +4065,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
|
resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
|
parse5-htmlparser2-tree-adapter@7.1.0:
|
||||||
|
resolution: {integrity: sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==}
|
||||||
|
|
||||||
|
parse5-parser-stream@7.1.2:
|
||||||
|
resolution: {integrity: sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==}
|
||||||
|
|
||||||
|
parse5@7.3.0:
|
||||||
|
resolution: {integrity: sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==}
|
||||||
|
|
||||||
path-exists@3.0.0:
|
path-exists@3.0.0:
|
||||||
resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
|
resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -4456,6 +4515,9 @@ packages:
|
|||||||
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
|
resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
safer-buffer@2.1.2:
|
||||||
|
resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
|
||||||
|
|
||||||
sax@1.1.4:
|
sax@1.1.4:
|
||||||
resolution: {integrity: sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==}
|
resolution: {integrity: sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==}
|
||||||
|
|
||||||
@@ -4891,6 +4953,10 @@ packages:
|
|||||||
undici-types@7.16.0:
|
undici-types@7.16.0:
|
||||||
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
||||||
|
|
||||||
|
undici@7.25.0:
|
||||||
|
resolution: {integrity: sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==}
|
||||||
|
engines: {node: '>=20.18.1'}
|
||||||
|
|
||||||
unicode-canonical-property-names-ecmascript@2.0.1:
|
unicode-canonical-property-names-ecmascript@2.0.1:
|
||||||
resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==}
|
resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -5009,6 +5075,15 @@ packages:
|
|||||||
resolution: {integrity: sha512-5ZZY1+lGq8LEKuDlg9M2RPJHlH3R7OVwyHqMcUsLKCgd9Wvf+QrFTCItkXXYPmrJn8H6gRLXbSgxLLdexiqHxw==}
|
resolution: {integrity: sha512-5ZZY1+lGq8LEKuDlg9M2RPJHlH3R7OVwyHqMcUsLKCgd9Wvf+QrFTCItkXXYPmrJn8H6gRLXbSgxLLdexiqHxw==}
|
||||||
engines: {node: '>=6.0.0', npm: '>=3.10.0'}
|
engines: {node: '>=6.0.0', npm: '>=3.10.0'}
|
||||||
|
|
||||||
|
whatwg-encoding@3.1.1:
|
||||||
|
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
deprecated: Use @exodus/bytes instead for a more spec-conformant and faster implementation
|
||||||
|
|
||||||
|
whatwg-mimetype@4.0.0:
|
||||||
|
resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
whatwg-url@5.0.0:
|
||||||
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
|
||||||
|
|
||||||
@@ -6228,6 +6303,10 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- typescript
|
- typescript
|
||||||
|
|
||||||
|
'@hono/node-server@2.0.0(hono@4.12.15)':
|
||||||
|
dependencies:
|
||||||
|
hono: 4.12.15
|
||||||
|
|
||||||
'@humanfs/core@0.19.1': {}
|
'@humanfs/core@0.19.1': {}
|
||||||
|
|
||||||
'@humanfs/node@0.16.7':
|
'@humanfs/node@0.16.7':
|
||||||
@@ -6637,15 +6716,15 @@ snapshots:
|
|||||||
|
|
||||||
'@polka/url@1.0.0-next.29': {}
|
'@polka/url@1.0.0-next.29': {}
|
||||||
|
|
||||||
'@pomade/core@0.2.3(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.13(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))':
|
'@pomade/core@0.2.3(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.15(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@frostr/bifrost': 1.0.7(typescript@5.9.3)
|
'@frostr/bifrost': 1.0.7(typescript@5.9.3)
|
||||||
'@noble/hashes': 2.0.1
|
'@noble/hashes': 2.0.1
|
||||||
'@peculiar/x509': 1.14.3
|
'@peculiar/x509': 1.14.3
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
'@welshman/net': 0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
||||||
'@welshman/signer': 0.8.13(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/signer': 0.8.15(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
'@welshman/util': 0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/util': 0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
cbor-x: 1.6.0
|
cbor-x: 1.6.0
|
||||||
hash-wasm: 4.12.0
|
hash-wasm: 4.12.0
|
||||||
nostr-tools: 2.20.0(typescript@5.9.3)
|
nostr-tools: 2.20.0(typescript@5.9.3)
|
||||||
@@ -7303,26 +7382,26 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@vite-pwa/assets-generator': 0.2.6
|
'@vite-pwa/assets-generator': 0.2.6
|
||||||
|
|
||||||
'@welshman/app@0.8.13(ed9ee8a79a580bcb9fa9bb6eb1a69558)':
|
'@welshman/app@0.8.15(ff026297546a8274624eb18a0ea86191)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@pomade/core': 0.2.3(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.13(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))
|
'@pomade/core': 0.2.3(@frostr/bifrost@1.0.7(typescript@5.9.3))(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/signer@0.8.15(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3)))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
'@welshman/feeds': 0.8.13(29451a19e278ea4a9cf66616f05d5557)
|
'@welshman/feeds': 0.8.15(6e55dcd4e7516745e7b0228620d35545)
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
'@welshman/net': 0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
||||||
'@welshman/router': 0.8.13(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))
|
'@welshman/router': 0.8.15(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))
|
||||||
'@welshman/signer': 0.8.13(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/signer': 0.8.15(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
'@welshman/store': 0.8.13(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(svelte@5.48.0)
|
'@welshman/store': 0.8.15(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(svelte@5.48.0)
|
||||||
'@welshman/util': 0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/util': 0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
fuse.js: 7.1.0
|
fuse.js: 7.1.0
|
||||||
svelte: 5.48.0
|
svelte: 5.48.0
|
||||||
throttle-debounce: 5.0.2
|
throttle-debounce: 5.0.2
|
||||||
|
|
||||||
'@welshman/content@0.8.13(nostr-tools@2.20.0(typescript@5.9.3))':
|
'@welshman/content@0.8.15(nostr-tools@2.20.0(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@braintree/sanitize-url': 7.1.1
|
'@braintree/sanitize-url': 7.1.1
|
||||||
nostr-tools: 2.20.0(typescript@5.9.3)
|
nostr-tools: 2.20.0(typescript@5.9.3)
|
||||||
|
|
||||||
'@welshman/editor@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-editor@1.1.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/extension-image@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)))(@tiptap/extension-link@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)(linkifyjs@4.3.2)(nostr-tools@2.20.0(typescript@5.9.3))(prosemirror-markdown@1.13.3)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.5)(tiptap-markdown@0.8.10(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))))(nostr-tools@2.20.0(typescript@5.9.3))':
|
'@welshman/editor@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-editor@1.1.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/extension-image@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)))(@tiptap/extension-link@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)(linkifyjs@4.3.2)(nostr-tools@2.20.0(typescript@5.9.3))(prosemirror-markdown@1.13.3)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.5)(tiptap-markdown@0.8.10(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))))(nostr-tools@2.20.0(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@tiptap/core': 2.27.2(@tiptap/pm@2.27.2)
|
'@tiptap/core': 2.27.2(@tiptap/pm@2.27.2)
|
||||||
'@tiptap/extension-code': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))
|
'@tiptap/extension-code': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))
|
||||||
@@ -7337,64 +7416,64 @@ snapshots:
|
|||||||
'@tiptap/extension-text': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))
|
'@tiptap/extension-text': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))
|
||||||
'@tiptap/pm': 2.27.2
|
'@tiptap/pm': 2.27.2
|
||||||
'@tiptap/suggestion': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)
|
'@tiptap/suggestion': 2.27.2(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/util': 0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/util': 0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
nostr-editor: 1.1.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/extension-image@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)))(@tiptap/extension-link@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)(linkifyjs@4.3.2)(nostr-tools@2.20.0(typescript@5.9.3))(prosemirror-markdown@1.13.3)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.5)(tiptap-markdown@0.8.10(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)))
|
nostr-editor: 1.1.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/extension-image@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)))(@tiptap/extension-link@2.27.1(@tiptap/core@2.27.2(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2))(@tiptap/pm@2.27.2)(linkifyjs@4.3.2)(nostr-tools@2.20.0(typescript@5.9.3))(prosemirror-markdown@1.13.3)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.5)(tiptap-markdown@0.8.10(@tiptap/core@2.27.2(@tiptap/pm@2.27.2)))
|
||||||
nostr-tools: 2.20.0(typescript@5.9.3)
|
nostr-tools: 2.20.0(typescript@5.9.3)
|
||||||
tippy.js: 6.3.7
|
tippy.js: 6.3.7
|
||||||
|
|
||||||
'@welshman/feeds@0.8.13(29451a19e278ea4a9cf66616f05d5557)':
|
'@welshman/feeds@0.8.15(6e55dcd4e7516745e7b0228620d35545)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
'@welshman/net': 0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
||||||
'@welshman/router': 0.8.13(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))
|
'@welshman/router': 0.8.15(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))
|
||||||
'@welshman/signer': 0.8.13(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/signer': 0.8.15(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
'@welshman/util': 0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/util': 0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
trava: 1.2.1
|
trava: 1.2.1
|
||||||
|
|
||||||
'@welshman/lib@0.8.13':
|
'@welshman/lib@0.8.15':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@scure/base': 1.2.6
|
'@scure/base': 1.2.6
|
||||||
'@types/events': 3.0.3
|
'@types/events': 3.0.3
|
||||||
events: 3.3.0
|
events: 3.3.0
|
||||||
|
|
||||||
'@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)':
|
'@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/util': 0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/util': 0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
events: 3.3.0
|
events: 3.3.0
|
||||||
isomorphic-ws: 5.0.0(ws@8.18.3)
|
isomorphic-ws: 5.0.0(ws@8.18.3)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- ws
|
- ws
|
||||||
|
|
||||||
'@welshman/router@0.8.13(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))':
|
'@welshman/router@0.8.15(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
'@welshman/net': 0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
||||||
'@welshman/util': 0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/util': 0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
|
|
||||||
'@welshman/signer@0.8.13(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3))':
|
'@welshman/signer@0.8.15(@noble/curves@1.9.7)(@noble/hashes@2.0.1)(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(nostr-signer-capacitor-plugin@https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1))(nostr-tools@2.20.0(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/curves': 1.9.7
|
'@noble/curves': 1.9.7
|
||||||
'@noble/hashes': 2.0.1
|
'@noble/hashes': 2.0.1
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
'@welshman/net': 0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
||||||
'@welshman/util': 0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/util': 0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
nostr-signer-capacitor-plugin: https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1)
|
nostr-signer-capacitor-plugin: https://codeload.github.com/coracle-social/nostr-signer-capacitor-plugin/tar.gz/be4bb90a1a15c8eec0934f2f66ce9e82ecc72d51(@capacitor/core@8.0.1)
|
||||||
nostr-tools: 2.20.0(typescript@5.9.3)
|
nostr-tools: 2.20.0(typescript@5.9.3)
|
||||||
|
|
||||||
'@welshman/store@0.8.13(@welshman/lib@0.8.13)(@welshman/net@0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(svelte@5.48.0)':
|
'@welshman/store@0.8.15(@welshman/lib@0.8.15)(@welshman/net@0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3))(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(svelte@5.48.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
'@welshman/net': 0.8.13(@welshman/lib@0.8.13)(@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
'@welshman/net': 0.8.15(@welshman/lib@0.8.15)(@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3)))(ws@8.18.3)
|
||||||
'@welshman/util': 0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3))
|
'@welshman/util': 0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3))
|
||||||
svelte: 5.48.0
|
svelte: 5.48.0
|
||||||
|
|
||||||
'@welshman/util@0.8.13(@noble/curves@1.9.7)(@welshman/lib@0.8.13)(nostr-tools@2.20.0(typescript@5.9.3))':
|
'@welshman/util@0.8.15(@noble/curves@1.9.7)(@welshman/lib@0.8.15)(nostr-tools@2.20.0(typescript@5.9.3))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@noble/curves': 1.9.7
|
'@noble/curves': 1.9.7
|
||||||
'@types/ws': 8.18.1
|
'@types/ws': 8.18.1
|
||||||
'@welshman/lib': 0.8.13
|
'@welshman/lib': 0.8.15
|
||||||
js-base64: 3.7.8
|
js-base64: 3.7.8
|
||||||
nostr-tools: 2.20.0(typescript@5.9.3)
|
nostr-tools: 2.20.0(typescript@5.9.3)
|
||||||
nostr-wasm: 0.1.0
|
nostr-wasm: 0.1.0
|
||||||
@@ -7642,6 +7721,29 @@ snapshots:
|
|||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
supports-color: 7.2.0
|
supports-color: 7.2.0
|
||||||
|
|
||||||
|
cheerio-select@2.1.0:
|
||||||
|
dependencies:
|
||||||
|
boolbase: 1.0.0
|
||||||
|
css-select: 5.2.2
|
||||||
|
css-what: 6.2.2
|
||||||
|
domelementtype: 2.3.0
|
||||||
|
domhandler: 5.0.3
|
||||||
|
domutils: 3.2.2
|
||||||
|
|
||||||
|
cheerio@1.2.0:
|
||||||
|
dependencies:
|
||||||
|
cheerio-select: 2.1.0
|
||||||
|
dom-serializer: 2.0.0
|
||||||
|
domhandler: 5.0.3
|
||||||
|
domutils: 3.2.2
|
||||||
|
encoding-sniffer: 0.2.1
|
||||||
|
htmlparser2: 10.1.0
|
||||||
|
parse5: 7.3.0
|
||||||
|
parse5-htmlparser2-tree-adapter: 7.1.0
|
||||||
|
parse5-parser-stream: 7.1.2
|
||||||
|
undici: 7.25.0
|
||||||
|
whatwg-mimetype: 4.0.0
|
||||||
|
|
||||||
chevrotain@7.1.1:
|
chevrotain@7.1.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
regexp-to-ast: 0.5.0
|
regexp-to-ast: 0.5.0
|
||||||
@@ -8028,10 +8130,17 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
sax: 1.1.4
|
sax: 1.1.4
|
||||||
|
|
||||||
|
emoji-picker-element-data@1.8.0: {}
|
||||||
|
|
||||||
emoji-picker-element@1.28.1: {}
|
emoji-picker-element@1.28.1: {}
|
||||||
|
|
||||||
emoji-regex@8.0.0: {}
|
emoji-regex@8.0.0: {}
|
||||||
|
|
||||||
|
encoding-sniffer@0.2.1:
|
||||||
|
dependencies:
|
||||||
|
iconv-lite: 0.6.3
|
||||||
|
whatwg-encoding: 3.1.1
|
||||||
|
|
||||||
enhanced-resolve@5.20.1:
|
enhanced-resolve@5.20.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
graceful-fs: 4.2.11
|
graceful-fs: 4.2.11
|
||||||
@@ -8041,6 +8150,10 @@ snapshots:
|
|||||||
|
|
||||||
entities@4.5.0: {}
|
entities@4.5.0: {}
|
||||||
|
|
||||||
|
entities@6.0.1: {}
|
||||||
|
|
||||||
|
entities@7.0.1: {}
|
||||||
|
|
||||||
env-paths@2.2.1: {}
|
env-paths@2.2.1: {}
|
||||||
|
|
||||||
env-paths@3.0.0: {}
|
env-paths@3.0.0: {}
|
||||||
@@ -8550,16 +8663,29 @@ snapshots:
|
|||||||
|
|
||||||
he@1.2.0: {}
|
he@1.2.0: {}
|
||||||
|
|
||||||
|
hono@4.12.15: {}
|
||||||
|
|
||||||
hosted-git-info@2.8.9: {}
|
hosted-git-info@2.8.9: {}
|
||||||
|
|
||||||
hosted-git-info@4.1.0:
|
hosted-git-info@4.1.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
lru-cache: 6.0.0
|
lru-cache: 6.0.0
|
||||||
|
|
||||||
|
htmlparser2@10.1.0:
|
||||||
|
dependencies:
|
||||||
|
domelementtype: 2.3.0
|
||||||
|
domhandler: 5.0.3
|
||||||
|
domutils: 3.2.2
|
||||||
|
entities: 7.0.1
|
||||||
|
|
||||||
husky@9.1.7: {}
|
husky@9.1.7: {}
|
||||||
|
|
||||||
ico-endec@0.1.6: {}
|
ico-endec@0.1.6: {}
|
||||||
|
|
||||||
|
iconv-lite@0.6.3:
|
||||||
|
dependencies:
|
||||||
|
safer-buffer: 2.1.2
|
||||||
|
|
||||||
idb@7.1.1: {}
|
idb@7.1.1: {}
|
||||||
|
|
||||||
idb@8.0.3: {}
|
idb@8.0.3: {}
|
||||||
@@ -9266,6 +9392,19 @@ snapshots:
|
|||||||
json-parse-even-better-errors: 2.3.1
|
json-parse-even-better-errors: 2.3.1
|
||||||
lines-and-columns: 1.2.4
|
lines-and-columns: 1.2.4
|
||||||
|
|
||||||
|
parse5-htmlparser2-tree-adapter@7.1.0:
|
||||||
|
dependencies:
|
||||||
|
domhandler: 5.0.3
|
||||||
|
parse5: 7.3.0
|
||||||
|
|
||||||
|
parse5-parser-stream@7.1.2:
|
||||||
|
dependencies:
|
||||||
|
parse5: 7.3.0
|
||||||
|
|
||||||
|
parse5@7.3.0:
|
||||||
|
dependencies:
|
||||||
|
entities: 6.0.1
|
||||||
|
|
||||||
path-exists@3.0.0: {}
|
path-exists@3.0.0: {}
|
||||||
|
|
||||||
path-exists@4.0.0: {}
|
path-exists@4.0.0: {}
|
||||||
@@ -9704,6 +9843,8 @@ snapshots:
|
|||||||
es-errors: 1.3.0
|
es-errors: 1.3.0
|
||||||
is-regex: 1.2.1
|
is-regex: 1.2.1
|
||||||
|
|
||||||
|
safer-buffer@2.1.2: {}
|
||||||
|
|
||||||
sax@1.1.4: {}
|
sax@1.1.4: {}
|
||||||
|
|
||||||
sax@1.4.4: {}
|
sax@1.4.4: {}
|
||||||
@@ -10229,6 +10370,8 @@ snapshots:
|
|||||||
|
|
||||||
undici-types@7.16.0: {}
|
undici-types@7.16.0: {}
|
||||||
|
|
||||||
|
undici@7.25.0: {}
|
||||||
|
|
||||||
unicode-canonical-property-names-ecmascript@2.0.1: {}
|
unicode-canonical-property-names-ecmascript@2.0.1: {}
|
||||||
|
|
||||||
unicode-match-property-ecmascript@2.0.0:
|
unicode-match-property-ecmascript@2.0.0:
|
||||||
@@ -10309,6 +10452,12 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
sdp: 3.2.1
|
sdp: 3.2.1
|
||||||
|
|
||||||
|
whatwg-encoding@3.1.1:
|
||||||
|
dependencies:
|
||||||
|
iconv-lite: 0.6.3
|
||||||
|
|
||||||
|
whatwg-mimetype@4.0.0: {}
|
||||||
|
|
||||||
whatwg-url@5.0.0:
|
whatwg-url@5.0.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
tr46: 0.0.3
|
tr46: 0.0.3
|
||||||
|
|||||||
@@ -0,0 +1,288 @@
|
|||||||
|
import path from "node:path"
|
||||||
|
import {promises as fs} from "node:fs"
|
||||||
|
import {fileURLToPath} from "node:url"
|
||||||
|
|
||||||
|
import "dotenv/config"
|
||||||
|
import {serve} from "@hono/node-server"
|
||||||
|
import {serveStatic} from "@hono/node-server/serve-static"
|
||||||
|
import {loadRelay} from "@welshman/app"
|
||||||
|
import {displayRelayUrl, normalizeRelayUrl} from "@welshman/util"
|
||||||
|
import {load} from "cheerio"
|
||||||
|
import {Hono} from "hono"
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
|
const __dirname = path.dirname(__filename)
|
||||||
|
|
||||||
|
const BUILD_DIR = path.join(__dirname, "build")
|
||||||
|
const INDEX_PATH = path.join(BUILD_DIR, "index.html")
|
||||||
|
|
||||||
|
const PORT = parseInt(process.env.PORT || "", 10) || 3000
|
||||||
|
const HOST = process.env.HOST || "0.0.0.0"
|
||||||
|
|
||||||
|
let TEMPLATE_HTML = ""
|
||||||
|
try {
|
||||||
|
TEMPLATE_HTML = await fs.readFile(INDEX_PATH, "utf8")
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Unable to read ${INDEX_PATH}. Run "pnpm run build" first.`)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
const PLATFORM_NAME = process.env.VITE_PLATFORM_NAME
|
||||||
|
const PLATFORM_DESCRIPTION = process.env.VITE_PLATFORM_DESCRIPTION
|
||||||
|
|
||||||
|
// Match client-side decode logic
|
||||||
|
const decodeRelay = url => {
|
||||||
|
try {
|
||||||
|
return normalizeRelayUrl(decodeURIComponent(url))
|
||||||
|
} catch {
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const requestUrlFromContext = context => {
|
||||||
|
const requestUrl = new URL(context.req.url)
|
||||||
|
const forwardedProto = context.req.header("x-forwarded-proto")?.split(",")[0]?.trim()
|
||||||
|
const forwardedHost = context.req.header("x-forwarded-host")?.split(",")[0]?.trim()
|
||||||
|
|
||||||
|
if (forwardedProto === "http" || forwardedProto === "https") {
|
||||||
|
requestUrl.protocol = `${forwardedProto}:`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (forwardedHost) {
|
||||||
|
requestUrl.host = forwardedHost
|
||||||
|
}
|
||||||
|
|
||||||
|
return requestUrl
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchRelayMeta = async relayUrl => {
|
||||||
|
if (!relayUrl) return undefined
|
||||||
|
try {
|
||||||
|
return await loadRelay(normalizeRelayUrl(relayUrl))
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Failed to fetch relay metadata for ${relayUrl}:`, err)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildDefaultImage = requestUrl => {
|
||||||
|
return new URL("/maskable-icon-512x512.png", requestUrl.origin).toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMetadataForInvite = async (url, match) => {
|
||||||
|
const relayParam = url.searchParams.get("r")
|
||||||
|
if (!relayParam) return undefined
|
||||||
|
|
||||||
|
const relayMetadata = await fetchRelayMeta(relayParam)
|
||||||
|
if (!relayMetadata) return undefined
|
||||||
|
|
||||||
|
const relayDisplay = displayRelayUrl(relayParam)
|
||||||
|
const spaceName = relayMetadata.name
|
||||||
|
const relayDescription = relayMetadata.description
|
||||||
|
|
||||||
|
const title = spaceName
|
||||||
|
? `Invite to ${spaceName} on ${PLATFORM_NAME}`
|
||||||
|
: `Invite to a Space on ${PLATFORM_NAME}`
|
||||||
|
|
||||||
|
const parts = []
|
||||||
|
if (spaceName) {
|
||||||
|
parts.push(`You are invited to join ${spaceName} on ${PLATFORM_NAME}.`)
|
||||||
|
} else {
|
||||||
|
parts.push(`You are invited to join a space on ${PLATFORM_NAME}.`)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (relayDisplay) parts.push(`Relay: ${relayDisplay}.`)
|
||||||
|
if (relayDescription) parts.push(relayDescription)
|
||||||
|
else parts.push(PLATFORM_DESCRIPTION)
|
||||||
|
|
||||||
|
const description = parts.join(" ")
|
||||||
|
const image =
|
||||||
|
relayMetadata.icon ||
|
||||||
|
relayMetadata.picture ||
|
||||||
|
relayMetadata.image ||
|
||||||
|
buildDefaultImage(url)
|
||||||
|
|
||||||
|
return {
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
image,
|
||||||
|
url: url.toString(),
|
||||||
|
site: url.origin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMetadataForSpace = async (url, match) => {
|
||||||
|
const relayParam = decodeRelay(match[1])
|
||||||
|
if (!relayParam) return undefined
|
||||||
|
|
||||||
|
const relayMetadata = await fetchRelayMeta(relayParam)
|
||||||
|
if (!relayMetadata) return undefined
|
||||||
|
|
||||||
|
const spaceName = relayMetadata.name || displayRelayUrl(relayParam)
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: `${spaceName} on ${PLATFORM_NAME}`,
|
||||||
|
description: relayMetadata.description || PLATFORM_DESCRIPTION,
|
||||||
|
image:
|
||||||
|
relayMetadata.icon ||
|
||||||
|
relayMetadata.picture ||
|
||||||
|
relayMetadata.image ||
|
||||||
|
buildDefaultImage(url),
|
||||||
|
url: url.toString(),
|
||||||
|
site: url.origin,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMetadataForSpaceSection = async (url, match) => {
|
||||||
|
const spaceMeta = await getMetadataForSpace(url, match)
|
||||||
|
if (!spaceMeta) return undefined
|
||||||
|
|
||||||
|
const section = match[2]
|
||||||
|
const sectionName = section.charAt(0).toUpperCase() + section.slice(1)
|
||||||
|
spaceMeta.title = `${sectionName} on ${spaceMeta.title}`
|
||||||
|
return spaceMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMetadataForSpaceItem = async (url, match) => {
|
||||||
|
const spaceMeta = await getMetadataForSpace(url, match)
|
||||||
|
if (!spaceMeta) return undefined
|
||||||
|
|
||||||
|
const section = match[2]
|
||||||
|
let itemType = "Item"
|
||||||
|
if (section === "calendar") itemType = "Event"
|
||||||
|
if (section === "threads") itemType = "Thread"
|
||||||
|
if (section === "polls") itemType = "Poll"
|
||||||
|
if (section === "goals") itemType = "Goal"
|
||||||
|
if (section === "classifieds") itemType = "Listing"
|
||||||
|
|
||||||
|
spaceMeta.title = `${itemType} on ${spaceMeta.title}`
|
||||||
|
return spaceMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
const getMetadataForRoom = async (url, match) => {
|
||||||
|
const spaceMeta = await getMetadataForSpace(url, match)
|
||||||
|
if (!spaceMeta) return undefined
|
||||||
|
|
||||||
|
// Room metadata requires fetching from Nostr, which can be added later.
|
||||||
|
spaceMeta.title = `Room on ${spaceMeta.title}`
|
||||||
|
return spaceMeta
|
||||||
|
}
|
||||||
|
|
||||||
|
const routes = [
|
||||||
|
[/^\/join\/?$/, getMetadataForInvite],
|
||||||
|
[/^\/spaces\/([^/]+)\/(calendar|chat|threads|polls|goals|classifieds|recent)\/?$/, getMetadataForSpaceSection],
|
||||||
|
[/^\/spaces\/([^/]+)\/(calendar|threads|polls|goals|classifieds)\/([^/]+)\/?$/, getMetadataForSpaceItem],
|
||||||
|
[/^\/spaces\/([^/]+)\/([^/]+)\/?$/, getMetadataForRoom],
|
||||||
|
[/^\/spaces\/([^/]+)\/?$/, getMetadataForSpace],
|
||||||
|
]
|
||||||
|
|
||||||
|
const getMetadataForRoute = async url => {
|
||||||
|
for (const [regex, getMetadata] of routes) {
|
||||||
|
const match = url.pathname.match(regex)
|
||||||
|
if (match) {
|
||||||
|
try {
|
||||||
|
return await getMetadata(url, match)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(`Error generating metadata for route ${url.pathname}:`, err)
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
const injectMeta = metadata => {
|
||||||
|
const $ = load(TEMPLATE_HTML)
|
||||||
|
|
||||||
|
if (metadata.title) {
|
||||||
|
$("title").text(metadata.title)
|
||||||
|
$('meta[property="og:title"]').attr("content", metadata.title)
|
||||||
|
$('meta[name="twitter:title"]').attr("content", metadata.title)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.description) {
|
||||||
|
$('meta[name="description"]').attr("content", metadata.description)
|
||||||
|
$('meta[property="og:description"]').attr("content", metadata.description)
|
||||||
|
$('meta[name="twitter:description"]').attr("content", metadata.description)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.image) {
|
||||||
|
$('meta[property="og:image"]').attr("content", metadata.image)
|
||||||
|
$('meta[name="twitter:image"]').attr("content", metadata.image)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.url) {
|
||||||
|
$('meta[property="og:url"]').attr("content", metadata.url)
|
||||||
|
$('meta[name="twitter:site"]').attr("content", metadata.site)
|
||||||
|
$('meta[name="twitter:url"]').attr("content", metadata.url)
|
||||||
|
$('link[rel="canonical"]').attr("href", metadata.url)
|
||||||
|
}
|
||||||
|
|
||||||
|
return $.html()
|
||||||
|
}
|
||||||
|
|
||||||
|
const app = new Hono()
|
||||||
|
|
||||||
|
// Only allow GET and HEAD requests
|
||||||
|
app.use("*", async (context, next) => {
|
||||||
|
const method = context.req.method
|
||||||
|
if (method !== "GET" && method !== "HEAD") {
|
||||||
|
return context.text("Method Not Allowed", 405, {Allow: "GET, HEAD"})
|
||||||
|
}
|
||||||
|
await next()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Serve static assets with appropriate caching
|
||||||
|
app.use(
|
||||||
|
"*",
|
||||||
|
serveStatic({
|
||||||
|
root: BUILD_DIR,
|
||||||
|
onFound: (filePath, context) => {
|
||||||
|
const isImmutable = filePath.split(path.sep).join("/").includes("/_app/immutable/")
|
||||||
|
const cacheControl =
|
||||||
|
path.basename(filePath) === "index.html"
|
||||||
|
? "no-cache"
|
||||||
|
: isImmutable
|
||||||
|
? "public, max-age=31536000, immutable"
|
||||||
|
: "public, max-age=3600"
|
||||||
|
|
||||||
|
context.header("Cache-Control", cacheControl)
|
||||||
|
|
||||||
|
// Immutable assets are content-hashed by Vite, so the filename is itself a
|
||||||
|
// stable content identifier. Exposing it as an ETag lets clients that
|
||||||
|
// revalidate explicitly (e.g. emoji-picker-element checks its data source
|
||||||
|
// on every load) skip re-downloading large files when nothing changed.
|
||||||
|
if (isImmutable) {
|
||||||
|
context.header("ETag", `"${path.basename(filePath)}"`)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
// SPA fallback for routes that don't match static files
|
||||||
|
app.get("*", async context => {
|
||||||
|
const requestUrl = requestUrlFromContext(context)
|
||||||
|
|
||||||
|
// If the path has an extension, it's likely a missing static asset, not an SPA route
|
||||||
|
if (path.extname(requestUrl.pathname)) {
|
||||||
|
return context.text("Not found", 404)
|
||||||
|
}
|
||||||
|
|
||||||
|
const metadata = await getMetadataForRoute(requestUrl)
|
||||||
|
const html = metadata ? injectMeta(metadata) : TEMPLATE_HTML
|
||||||
|
|
||||||
|
return context.html(html, 200, {
|
||||||
|
"Cache-Control": metadata ? "no-store" : "no-cache",
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
serve(
|
||||||
|
{
|
||||||
|
fetch: app.fetch,
|
||||||
|
hostname: HOST,
|
||||||
|
port: PORT,
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
console.log(`Flotilla server running on http://${HOST}:${PORT}`)
|
||||||
|
},
|
||||||
|
)
|
||||||
+14
-1
@@ -235,6 +235,7 @@
|
|||||||
|
|
||||||
:root {
|
:root {
|
||||||
font-family: Lato;
|
font-family: Lato;
|
||||||
|
text-size-adjust: 100%;
|
||||||
--sait: var(--safe-area-inset-top, env(safe-area-inset-top));
|
--sait: var(--safe-area-inset-top, env(safe-area-inset-top));
|
||||||
--saib: var(--safe-area-inset-bottom, env(safe-area-inset-bottom));
|
--saib: var(--safe-area-inset-bottom, env(safe-area-inset-bottom));
|
||||||
--sail: var(--safe-area-inset-left, env(safe-area-inset-left));
|
--sail: var(--safe-area-inset-left, env(safe-area-inset-left));
|
||||||
@@ -332,7 +333,7 @@
|
|||||||
|
|
||||||
.input-editor .tiptap {
|
.input-editor .tiptap {
|
||||||
--tiptap-object-bg: var(--color-base-200);
|
--tiptap-object-bg: var(--color-base-200);
|
||||||
@apply input h-auto p-[.65rem];
|
@apply input block h-auto p-[.65rem];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* link-content, based on tiptap */
|
/* link-content, based on tiptap */
|
||||||
@@ -416,12 +417,24 @@ progress[value]::-webkit-progress-value {
|
|||||||
@apply md:left-[calc(18.5rem+var(--sail))];
|
@apply md:left-[calc(18.5rem+var(--sail))];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.left-content-full {
|
||||||
|
@apply md:left-[calc(3.5rem+var(--sail))];
|
||||||
|
}
|
||||||
|
|
||||||
/* Keyboard open state adjustments */
|
/* Keyboard open state adjustments */
|
||||||
|
|
||||||
|
body.keyboard-open {
|
||||||
|
--saib: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
body.keyboard-open .hide-on-keyboard {
|
body.keyboard-open .hide-on-keyboard {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.keyboard-open .chat__compose {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* chat view */
|
/* chat view */
|
||||||
|
|
||||||
.chat__compose {
|
.chat__compose {
|
||||||
|
|||||||
+7
-4
@@ -2,15 +2,18 @@
|
|||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
<title>{NAME}</title>
|
||||||
|
<link rel="canonical" href="{URL}" />
|
||||||
<meta
|
<meta
|
||||||
name="viewport"
|
name="viewport"
|
||||||
content="width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content" />
|
content="width=device-width, initial-scale=1.0, viewport-fit=cover, interactive-widget=resizes-content" />
|
||||||
<meta name="theme-color" content="{ACCENT}" />
|
<meta name="theme-color" content="{ACCENT}" />
|
||||||
<meta name="description" content="{DESCRIPTION}" />
|
<meta name="description" content="{DESCRIPTION}" />
|
||||||
<meta name="og:url" content="{URL}" />
|
<meta property="og:url" content="{URL}" />
|
||||||
<meta name="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
<meta name="og:title" content="{NAME}" />
|
<meta property="og:title" content="{NAME}" />
|
||||||
<meta name="og:description" content="{DESCRIPTION}" />
|
<meta property="og:description" content="{DESCRIPTION}" />
|
||||||
|
<meta property="og:image" content="" />
|
||||||
<meta name="twitter:card" content="summary" />
|
<meta name="twitter:card" content="summary" />
|
||||||
<meta name="twitter:site" content="{URL}" />
|
<meta name="twitter:site" content="{URL}" />
|
||||||
<meta name="twitter:title" content="{NAME}" />
|
<meta name="twitter:title" content="{NAME}" />
|
||||||
|
|||||||
@@ -41,6 +41,21 @@ export const participantKey = (p: VoiceParticipant) => p.pubkey ?? p.identity
|
|||||||
|
|
||||||
export const speakingParticipants = writable<VoiceParticipant[]>([])
|
export const speakingParticipants = writable<VoiceParticipant[]>([])
|
||||||
|
|
||||||
|
export const participantMediaState = writable<Map<string, {muted: boolean; cameraOn: boolean}>>(
|
||||||
|
new Map(),
|
||||||
|
)
|
||||||
|
|
||||||
|
export const mediaStateByIdentity = derived(
|
||||||
|
[participantMediaState, currentVoiceSession],
|
||||||
|
([$media, $session]) =>
|
||||||
|
(identity: string) => {
|
||||||
|
if ($session?.room.localParticipant.identity === identity) {
|
||||||
|
return {muted: $session.muted, cameraOn: $session.cameraOn}
|
||||||
|
}
|
||||||
|
return $media.get(identity) ?? {muted: true, cameraOn: false}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|
||||||
export const isParticipantSpeaking = derived(
|
export const isParticipantSpeaking = derived(
|
||||||
speakingParticipants,
|
speakingParticipants,
|
||||||
$participants => (p: VoiceParticipant) =>
|
$participants => (p: VoiceParticipant) =>
|
||||||
|
|||||||
+36
-1
@@ -6,9 +6,11 @@ import {
|
|||||||
DisconnectReason,
|
DisconnectReason,
|
||||||
LocalParticipant,
|
LocalParticipant,
|
||||||
LocalTrackPublication,
|
LocalTrackPublication,
|
||||||
|
Participant,
|
||||||
Room as LiveKitRoom,
|
Room as LiveKitRoom,
|
||||||
RoomEvent,
|
RoomEvent,
|
||||||
Track,
|
Track,
|
||||||
|
TrackPublication,
|
||||||
supportsAudioOutputSelection,
|
supportsAudioOutputSelection,
|
||||||
type AudioCaptureOptions,
|
type AudioCaptureOptions,
|
||||||
} from "livekit-client"
|
} from "livekit-client"
|
||||||
@@ -24,6 +26,7 @@ import {
|
|||||||
currentVoiceSession,
|
currentVoiceSession,
|
||||||
participantFromLiveKitIdentity,
|
participantFromLiveKitIdentity,
|
||||||
participantKey,
|
participantKey,
|
||||||
|
participantMediaState,
|
||||||
participantPubkeyMap,
|
participantPubkeyMap,
|
||||||
pubkeyFromLiveKitIdentity,
|
pubkeyFromLiveKitIdentity,
|
||||||
speakingParticipants,
|
speakingParticipants,
|
||||||
@@ -89,6 +92,27 @@ const deleteParticipant = (identity: string) => {
|
|||||||
next.delete(identity)
|
next.delete(identity)
|
||||||
return next
|
return next
|
||||||
})
|
})
|
||||||
|
participantMediaState.update(m => {
|
||||||
|
if (!m.has(identity)) return m
|
||||||
|
const next = new Map(m)
|
||||||
|
next.delete(identity)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const syncParticipantMedia = (participant: Participant) => {
|
||||||
|
const state = {muted: !participant.isMicrophoneEnabled, cameraOn: participant.isCameraEnabled}
|
||||||
|
participantMediaState.update(m => {
|
||||||
|
const prev = m.get(participant.identity)
|
||||||
|
if (prev?.muted === state.muted && prev?.cameraOn === state.cameraOn) return m
|
||||||
|
const next = new Map(m)
|
||||||
|
next.set(participant.identity, state)
|
||||||
|
return next
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const onParticipantMediaChanged = (_publication: TrackPublication, participant: Participant) => {
|
||||||
|
syncParticipantMedia(participant)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchLivekitToken = async (
|
const fetchLivekitToken = async (
|
||||||
@@ -185,6 +209,7 @@ const onRoomDisconnected = (reason?: DisconnectReason) => {
|
|||||||
}
|
}
|
||||||
speakingParticipants.set([])
|
speakingParticipants.set([])
|
||||||
participantPubkeyMap.set(new Map())
|
participantPubkeyMap.set(new Map())
|
||||||
|
participantMediaState.set(new Map())
|
||||||
}
|
}
|
||||||
|
|
||||||
const onTrackSubscribed = (track: Track) => {
|
const onTrackSubscribed = (track: Track) => {
|
||||||
@@ -214,8 +239,9 @@ const playJoinSound = () => {
|
|||||||
audio.play().catch(() => {})
|
audio.play().catch(() => {})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onParticipantConnected = (participant: {identity: string}) => {
|
const onParticipantConnected = (participant: Participant) => {
|
||||||
addParticipant(participant.identity)
|
addParticipant(participant.identity)
|
||||||
|
syncParticipantMedia(participant)
|
||||||
playJoinSound()
|
playJoinSound()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +299,11 @@ export const joinVoiceRoom = async (
|
|||||||
liveKitRoom.on(RoomEvent.TrackUnsubscribed, onTrackUnsubscribed)
|
liveKitRoom.on(RoomEvent.TrackUnsubscribed, onTrackUnsubscribed)
|
||||||
liveKitRoom.on(RoomEvent.LocalTrackUnpublished, onLocalTrackUnpublished)
|
liveKitRoom.on(RoomEvent.LocalTrackUnpublished, onLocalTrackUnpublished)
|
||||||
liveKitRoom.on(RoomEvent.ActiveSpeakersChanged, onActiveSpeakersChanged)
|
liveKitRoom.on(RoomEvent.ActiveSpeakersChanged, onActiveSpeakersChanged)
|
||||||
|
liveKitRoom.on(RoomEvent.TrackMuted, onParticipantMediaChanged)
|
||||||
|
liveKitRoom.on(RoomEvent.TrackUnmuted, onParticipantMediaChanged)
|
||||||
|
liveKitRoom.on(RoomEvent.TrackPublished, onParticipantMediaChanged)
|
||||||
|
liveKitRoom.on(RoomEvent.TrackUnpublished, onParticipantMediaChanged)
|
||||||
|
liveKitRoom.on(RoomEvent.LocalTrackPublished, onParticipantMediaChanged)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Promise.race([
|
await Promise.race([
|
||||||
@@ -288,9 +319,12 @@ export const joinVoiceRoom = async (
|
|||||||
}
|
}
|
||||||
|
|
||||||
participantPubkeyMap.set(new Map())
|
participantPubkeyMap.set(new Map())
|
||||||
|
participantMediaState.set(new Map())
|
||||||
addParticipant(liveKitRoom.localParticipant.identity)
|
addParticipant(liveKitRoom.localParticipant.identity)
|
||||||
|
syncParticipantMedia(liveKitRoom.localParticipant)
|
||||||
for (const p of liveKitRoom.remoteParticipants.values()) {
|
for (const p of liveKitRoom.remoteParticipants.values()) {
|
||||||
addParticipant(p.identity)
|
addParticipant(p.identity)
|
||||||
|
syncParticipantMedia(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
const muted = await setUpMicrophone(startMuted, preferredMicId, liveKitRoom.localParticipant)
|
const muted = await setUpMicrophone(startMuted, preferredMicId, liveKitRoom.localParticipant)
|
||||||
@@ -344,6 +378,7 @@ export const leaveVoiceRoom = async () => {
|
|||||||
session.room.disconnect()
|
session.room.disconnect()
|
||||||
speakingParticipants.set([])
|
speakingParticipants.set([])
|
||||||
participantPubkeyMap.set(new Map())
|
participantPubkeyMap.set(new Map())
|
||||||
|
participantMediaState.set(new Map())
|
||||||
}
|
}
|
||||||
|
|
||||||
export const rejoinVoiceRoom = async (): Promise<void> => {
|
export const rejoinVoiceRoom = async (): Promise<void> => {
|
||||||
|
|||||||
@@ -19,15 +19,17 @@
|
|||||||
const end = $derived(parseInt(meta.end))
|
const end = $derived(parseInt(meta.end))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex grow flex-wrap justify-between gap-2">
|
<div class="flex flex-col justify-between gap-1">
|
||||||
<p class="text-xl">{meta.title || meta.name}</p>
|
<p class="text-lg">{meta.title || meta.name}</p>
|
||||||
{#if !isNaN(start) && !isNaN(end)}
|
{#if !isNaN(start) && !isNaN(end)}
|
||||||
{@const startDateDisplay = formatTimestampAsDate(start)}
|
{@const startDateDisplay = formatTimestampAsDate(start)}
|
||||||
{@const endDateDisplay = formatTimestampAsDate(end)}
|
{@const endDateDisplay = formatTimestampAsDate(end)}
|
||||||
{@const isSingleDay = startDateDisplay === endDateDisplay}
|
{@const isSingleDay = startDateDisplay === endDateDisplay}
|
||||||
<div class="flex items-center gap-2 text-sm">
|
<div class="flex flex-wrap gap-2 text-xs">
|
||||||
<Icon icon={ClockCircle} size={4} />
|
<div class="flex items-center gap-2">
|
||||||
<span class="hidden sm:block">{formatTimestampAsDate(start)}</span>
|
<Icon icon={ClockCircle} size={4} />
|
||||||
|
{formatTimestampAsDate(start)}
|
||||||
|
</div>
|
||||||
{formatTimestampAsTime(start)} — {isSingleDay
|
{formatTimestampAsTime(start)} — {isSingleDay
|
||||||
? formatTimestampAsTime(end)
|
? formatTimestampAsTime(end)
|
||||||
: formatTimestamp(end)}
|
: formatTimestamp(end)}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@
|
|||||||
import ChatComposeEdit from "@app/components/ChatComposeEdit.svelte"
|
import ChatComposeEdit from "@app/components/ChatComposeEdit.svelte"
|
||||||
import ChatComposeParent from "@app/components/ChatComposeParent.svelte"
|
import ChatComposeParent from "@app/components/ChatComposeParent.svelte"
|
||||||
import ThunkToast from "@app/components/ThunkToast.svelte"
|
import ThunkToast from "@app/components/ThunkToast.svelte"
|
||||||
import {userSettingsValues, deriveChat} from "@app/core/state"
|
import {userSettingsValues, deriveChat, makeChatId} from "@app/core/state"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
import {DraftKey} from "@app/util/drafts"
|
import {DraftKey} from "@app/util/drafts"
|
||||||
import {makeDelete, prependParent} from "@app/core/commands"
|
import {makeDelete, prependParent} from "@app/core/commands"
|
||||||
@@ -66,8 +66,9 @@
|
|||||||
|
|
||||||
const {pubkeys, info}: Props = $props()
|
const {pubkeys, info}: Props = $props()
|
||||||
|
|
||||||
const chat = deriveChat(pubkeys)
|
const chatId = makeChatId(pubkeys)
|
||||||
const draftKey = new DraftKey<{content?: string | object}>(`dm:${$chat?.id}`)
|
const chat = deriveChat(chatId)
|
||||||
|
const draftKey = new DraftKey<{content?: string | object}>(`dm:${chatId}`)
|
||||||
const others = remove($pubkey!, pubkeys)
|
const others = remove($pubkey!, pubkeys)
|
||||||
const missingRelayLists = $derived(others.filter(pk => !$messagingRelayListsByPubkey.has(pk)))
|
const missingRelayLists = $derived(others.filter(pk => !$messagingRelayListsByPubkey.has(pk)))
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
truncate,
|
truncate,
|
||||||
renderAsHtml,
|
renderAsHtml,
|
||||||
isText,
|
isText,
|
||||||
|
isEmail,
|
||||||
isEmoji,
|
isEmoji,
|
||||||
isTopic,
|
isTopic,
|
||||||
isCode,
|
isCode,
|
||||||
@@ -26,6 +27,7 @@
|
|||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import ContentToken from "@app/components/ContentToken.svelte"
|
import ContentToken from "@app/components/ContentToken.svelte"
|
||||||
import ContentEmoji from "@app/components/ContentEmoji.svelte"
|
import ContentEmoji from "@app/components/ContentEmoji.svelte"
|
||||||
|
import ContentEmail from "@app/components/ContentEmail.svelte"
|
||||||
import ContentCode from "@app/components/ContentCode.svelte"
|
import ContentCode from "@app/components/ContentCode.svelte"
|
||||||
import ContentLinkInline from "@app/components/ContentLinkInline.svelte"
|
import ContentLinkInline from "@app/components/ContentLinkInline.svelte"
|
||||||
import ContentLinkBlock from "@app/components/ContentLinkBlock.svelte"
|
import ContentLinkBlock from "@app/components/ContentLinkBlock.svelte"
|
||||||
@@ -159,6 +161,8 @@
|
|||||||
<ContentTopic value={parsed.value} />
|
<ContentTopic value={parsed.value} />
|
||||||
{:else if isEmoji(parsed)}
|
{:else if isEmoji(parsed)}
|
||||||
<ContentEmoji value={parsed.value} />
|
<ContentEmoji value={parsed.value} />
|
||||||
|
{:else if isEmail(parsed)}
|
||||||
|
<ContentEmail value={parsed.value} />
|
||||||
{:else if isCode(parsed)}
|
{:else if isCode(parsed)}
|
||||||
<ContentCode
|
<ContentCode
|
||||||
value={parsed.value}
|
value={parsed.value}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import LinkRound from "@assets/icons/link-round.svg?dataurl"
|
||||||
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
|
import Link from "@lib/components/Link.svelte"
|
||||||
|
|
||||||
|
export let value: string
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<Link external href="mailto:{value}">
|
||||||
|
<Icon icon={LinkRound} size={3} />
|
||||||
|
{value}
|
||||||
|
</Link>
|
||||||
@@ -7,6 +7,7 @@
|
|||||||
renderAsHtml,
|
renderAsHtml,
|
||||||
isText,
|
isText,
|
||||||
isEmoji,
|
isEmoji,
|
||||||
|
isEmail,
|
||||||
isTopic,
|
isTopic,
|
||||||
isCode,
|
isCode,
|
||||||
isCashu,
|
isCashu,
|
||||||
@@ -24,6 +25,7 @@
|
|||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import ContentToken from "@app/components/ContentToken.svelte"
|
import ContentToken from "@app/components/ContentToken.svelte"
|
||||||
import ContentEmoji from "@app/components/ContentEmoji.svelte"
|
import ContentEmoji from "@app/components/ContentEmoji.svelte"
|
||||||
|
import ContentEmail from "@app/components/ContentEmail.svelte"
|
||||||
import ContentCode from "@app/components/ContentCode.svelte"
|
import ContentCode from "@app/components/ContentCode.svelte"
|
||||||
import ContentLinkInline from "@app/components/ContentLinkInline.svelte"
|
import ContentLinkInline from "@app/components/ContentLinkInline.svelte"
|
||||||
import ContentNewline from "@app/components/ContentNewline.svelte"
|
import ContentNewline from "@app/components/ContentNewline.svelte"
|
||||||
@@ -109,6 +111,8 @@
|
|||||||
<ContentTopic value={parsed.value} />
|
<ContentTopic value={parsed.value} />
|
||||||
{:else if isEmoji(parsed)}
|
{:else if isEmoji(parsed)}
|
||||||
<ContentEmoji value={parsed.value} />
|
<ContentEmoji value={parsed.value} />
|
||||||
|
{:else if isEmail(parsed)}
|
||||||
|
<ContentEmail value={parsed.value} />
|
||||||
{:else if isCode(parsed)}
|
{:else if isCode(parsed)}
|
||||||
<ContentCode
|
<ContentCode
|
||||||
value={parsed.value}
|
value={parsed.value}
|
||||||
|
|||||||
@@ -42,7 +42,7 @@
|
|||||||
let popover: Instance | undefined = $state()
|
let popover: Instance | undefined = $state()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Button class="join rounded-full">
|
<div class="join items-center rounded-full">
|
||||||
{#if ENABLE_ZAPS && !hideZap}
|
{#if ENABLE_ZAPS && !hideZap}
|
||||||
<ZapButton {url} {event} class="btn join-item btn-neutral btn-xs">
|
<ZapButton {url} {event} class="btn join-item btn-neutral btn-xs">
|
||||||
<Icon icon={Bolt} size={4} />
|
<Icon icon={Bolt} size={4} />
|
||||||
@@ -52,6 +52,7 @@
|
|||||||
<Icon icon={SmileCircle} size={4} />
|
<Icon icon={SmileCircle} size={4} />
|
||||||
</EmojiButton>
|
</EmojiButton>
|
||||||
<Tippy
|
<Tippy
|
||||||
|
class="flex"
|
||||||
bind:popover
|
bind:popover
|
||||||
component={EventMenu}
|
component={EventMenu}
|
||||||
props={{url, noun, event, customActions, onClick: hidePopover}}
|
props={{url, noun, event, customActions, onClick: hidePopover}}
|
||||||
@@ -60,4 +61,4 @@
|
|||||||
<Icon icon={MenuDots} size={4} />
|
<Icon icon={MenuDots} size={4} />
|
||||||
</Button>
|
</Button>
|
||||||
</Tippy>
|
</Tippy>
|
||||||
</Button>
|
</div>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||||
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
|
import ProgressBar from "@app/components/ProgressBar.svelte"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
import {PLATFORM_NAME} from "@app/core/state"
|
import {PLATFORM_NAME} from "@app/core/state"
|
||||||
|
|
||||||
@@ -22,9 +23,11 @@
|
|||||||
secret: string
|
secret: string
|
||||||
next: () => unknown
|
next: () => unknown
|
||||||
submitText?: string
|
submitText?: string
|
||||||
|
step?: number
|
||||||
|
totalSteps?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const {secret, next, submitText = "Continue"}: Props = $props()
|
const {secret, next, submitText = "Continue", step, totalSteps}: Props = $props()
|
||||||
|
|
||||||
const back = () => history.back()
|
const back = () => history.back()
|
||||||
|
|
||||||
@@ -150,6 +153,9 @@
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
{#if step && totalSteps}
|
||||||
|
<ProgressBar current={step} total={totalSteps} />
|
||||||
|
{/if}
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button class="btn btn-link" onclick={back}>
|
<Button class="btn btn-link" onclick={back}>
|
||||||
<Icon icon={AltArrowLeft} />
|
<Icon icon={AltArrowLeft} />
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
import LogInOTP from "@app/components/LogInOTP.svelte"
|
import LogInOTP from "@app/components/LogInOTP.svelte"
|
||||||
import LogInSelect from "@app/components/LogInSelect.svelte"
|
import LogInSelect from "@app/components/LogInSelect.svelte"
|
||||||
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade"
|
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade"
|
||||||
|
import {getPomadeLoginFailureMessage, POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
|
||||||
import {pushModal, clearModals} from "@app/util/modal"
|
import {pushModal, clearModals} from "@app/util/modal"
|
||||||
import {setChecked} from "@app/util/notifications"
|
import {setChecked} from "@app/util/notifications"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
|
|
||||||
return pushToast({
|
return pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: "Sorry, we were unable to log you in.",
|
message: getPomadeLoginFailureMessage(messages),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,10 +65,17 @@
|
|||||||
|
|
||||||
pushToast({
|
pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: "Sorry, we were unable to log you in.",
|
message: getPomadeLoginFailureMessage(res.messages),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Login error:", error)
|
||||||
|
|
||||||
|
pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: POMADE_NETWORK_ERROR_MESSAGE,
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
@@ -90,7 +98,7 @@
|
|||||||
{#snippet input()}
|
{#snippet input()}
|
||||||
<label class="input input-bordered flex w-full items-center gap-2">
|
<label class="input input-bordered flex w-full items-center gap-2">
|
||||||
<Icon icon={Letter} />
|
<Icon icon={Letter} />
|
||||||
<input bind:value={email} />
|
<input type="email" bind:value={email} />
|
||||||
</label>
|
</label>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</FieldInline>
|
</FieldInline>
|
||||||
|
|||||||
@@ -15,6 +15,7 @@
|
|||||||
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
import LogInOTPConfirm from "@app/components/LogInOTPConfirm.svelte"
|
import LogInOTPConfirm from "@app/components/LogInOTPConfirm.svelte"
|
||||||
|
import {POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
|
|
||||||
@@ -35,11 +36,20 @@
|
|||||||
if (ok) {
|
if (ok) {
|
||||||
pushModal(LogInOTPConfirm, {email, peersByPrefix})
|
pushModal(LogInOTPConfirm, {email, peersByPrefix})
|
||||||
} else {
|
} else {
|
||||||
|
console.error("Pomade challenge request failed during OTP login")
|
||||||
|
|
||||||
pushToast({
|
pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: "Sorry, we were unable to request a login code.",
|
message: POMADE_NETWORK_ERROR_MESSAGE,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
|
||||||
|
pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: POMADE_NETWORK_ERROR_MESSAGE,
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
@@ -61,7 +71,7 @@
|
|||||||
{#snippet input()}
|
{#snippet input()}
|
||||||
<label class="input input-bordered flex w-full items-center gap-2">
|
<label class="input input-bordered flex w-full items-center gap-2">
|
||||||
<Icon icon={Letter} />
|
<Icon icon={Letter} />
|
||||||
<input bind:value={email} />
|
<input type="email" bind:value={email} />
|
||||||
</label>
|
</label>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</FieldInline>
|
</FieldInline>
|
||||||
|
|||||||
@@ -15,10 +15,11 @@
|
|||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
import StringMultiInput from "@lib/components/StringMultiInput.svelte"
|
import StringMultiInput from "@lib/components/StringMultiInput.svelte"
|
||||||
import LogInSelect from "@app/components/LogInSelect.svelte"
|
import LogInSelect from "@app/components/LogInSelect.svelte"
|
||||||
import {pushToast} from "@app/util/toast"
|
|
||||||
import {setChecked} from "@app/util/notifications"
|
|
||||||
import {pushModal, clearModals} from "@app/util/modal"
|
import {pushModal, clearModals} from "@app/util/modal"
|
||||||
|
import {setChecked} from "@app/util/notifications"
|
||||||
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade"
|
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade"
|
||||||
|
import {getPomadeLoginFailureMessage, POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
|
||||||
|
import {pushToast} from "@app/util/toast"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
email: string
|
email: string
|
||||||
@@ -44,7 +45,7 @@
|
|||||||
|
|
||||||
return pushToast({
|
return pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: "Sorry, we were unable to log you in.",
|
message: getPomadeLoginFailureMessage(messages),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,10 +65,17 @@
|
|||||||
|
|
||||||
pushToast({
|
pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: "Sorry, we were unable to log you in.",
|
message: getPomadeLoginFailureMessage(res.messages),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Login error:", error)
|
||||||
|
|
||||||
|
pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: POMADE_NETWORK_ERROR_MESSAGE,
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
import Profile from "@app/components/Profile.svelte"
|
import Profile from "@app/components/Profile.svelte"
|
||||||
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade"
|
import {deleteDeactivatedPomadeSessions, loginWithPomade} from "@app/util/pomade"
|
||||||
|
import {getPomadeLoginFailureMessage, POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
|
||||||
import {setChecked} from "@app/util/notifications"
|
import {setChecked} from "@app/util/notifications"
|
||||||
import {clearModals} from "@app/util/modal"
|
import {clearModals} from "@app/util/modal"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
@@ -46,9 +47,16 @@
|
|||||||
|
|
||||||
pushToast({
|
pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: "Sorry, we were unable to log you in.",
|
message: getPomadeLoginFailureMessage(res.messages),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Login error:", error)
|
||||||
|
|
||||||
|
pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: POMADE_NETWORK_ERROR_MESSAGE,
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,13 +13,16 @@
|
|||||||
|
|
||||||
const onClick = () => goToSpace(url)
|
const onClick = () => goToSpace(url)
|
||||||
|
|
||||||
|
const path = makeSpacePath(url)
|
||||||
|
|
||||||
const display = $derived(deriveRelayDisplay(url))
|
const display = $derived(deriveRelayDisplay(url))
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<PrimaryNavItem
|
<PrimaryNavItem
|
||||||
|
href={path}
|
||||||
onclick={onClick}
|
onclick={onClick}
|
||||||
title={$display}
|
title={$display}
|
||||||
class="tooltip-right"
|
class="tooltip-right"
|
||||||
notification={$notifications.has(makeSpacePath(url))}>
|
notification={$notifications.has(path)}>
|
||||||
<RelayIcon {url} size={10} class="rounded-full" />
|
<RelayIcon {url} size={10} class="rounded-full" />
|
||||||
</PrimaryNavItem>
|
</PrimaryNavItem>
|
||||||
|
|||||||
@@ -23,9 +23,10 @@
|
|||||||
onsubmit: (values: Values) => void
|
onsubmit: (values: Values) => void
|
||||||
isSignup?: boolean
|
isSignup?: boolean
|
||||||
footer: Snippet
|
footer: Snippet
|
||||||
|
progressBar?: Snippet
|
||||||
}
|
}
|
||||||
|
|
||||||
const {initialValues, isSignup, onsubmit, footer}: Props = $props()
|
const {initialValues, isSignup, onsubmit, footer, progressBar}: Props = $props()
|
||||||
|
|
||||||
const values = $state(initialValues)
|
const values = $state(initialValues)
|
||||||
|
|
||||||
@@ -103,6 +104,9 @@
|
|||||||
</Field>
|
</Field>
|
||||||
{/if}
|
{/if}
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
{#if progressBar}
|
||||||
|
{@render progressBar()}
|
||||||
|
{/if}
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
{@render footer()}
|
{@render footer()}
|
||||||
</ModalFooter>
|
</ModalFooter>
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
const {current, total}: {current: number; total: number} = $props()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex w-full">
|
||||||
|
{#each Array(total) as _, i}
|
||||||
|
<div class="h-1 flex-1 transition-colors {i < current ? 'bg-primary' : 'bg-base-300'}"></div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
@@ -19,12 +19,12 @@
|
|||||||
|
|
||||||
<div class="col-4 text-left">
|
<div class="col-4 text-left">
|
||||||
<div class="col-2">
|
<div class="col-2">
|
||||||
<div class="relative flex gap-4">
|
<div class="relative flex gap-2 sm:gap-4">
|
||||||
<div class="relative">
|
<div class="relative">
|
||||||
<div class="avatar relative">
|
<div class="avatar relative">
|
||||||
<div
|
<div
|
||||||
class="center flex! h-12 w-12 min-w-12 rounded-full border-2 border-solid border-base-300 bg-base-300">
|
class="center flex! h-12 w-12 min-w-12 rounded-full border-2 border-solid border-base-300 bg-base-300">
|
||||||
<RelayIcon {url} />
|
<RelayIcon {url} size={10} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{#if $rooms.includes(url)}
|
{#if $rooms.includes(url)}
|
||||||
@@ -36,13 +36,11 @@
|
|||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="min-w-0">
|
<div class="min-w-0">
|
||||||
<h2 class="ellipsize whitespace-nowrap text-xl">
|
<RelayName {url} class="ellipsize whitespace-nowrap text-lg sm:text-xl" />
|
||||||
<RelayName {url} />
|
<p class="text-xs sm:text-sm opacity-75">{url}</p>
|
||||||
</h2>
|
|
||||||
<p class="text-sm opacity-75">{url}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<RelayDescription {url} />
|
<RelayDescription {url} class="text-sm sm:text-md" />
|
||||||
</div>
|
</div>
|
||||||
{#if !hideFavorites && $favorited.size > 0}
|
{#if !hideFavorites && $favorited.size > 0}
|
||||||
<div class="row-2 card2 card2-sm bg-alt">
|
<div class="row-2 card2 card2-sm bg-alt">
|
||||||
|
|||||||
@@ -68,8 +68,6 @@
|
|||||||
const content = ed.getText({blockSeparator: "\n"}).trim()
|
const content = ed.getText({blockSeparator: "\n"}).trim()
|
||||||
const tags = ed.storage.nostr.getEditorTags()
|
const tags = ed.storage.nostr.getEditorTags()
|
||||||
|
|
||||||
if (!content) return
|
|
||||||
|
|
||||||
onSubmit({content, tags})
|
onSubmit({content, tags})
|
||||||
|
|
||||||
draftKey?.clear()
|
draftKey?.clear()
|
||||||
|
|||||||
@@ -62,9 +62,10 @@
|
|||||||
|
|
||||||
const flows = {
|
const flows = {
|
||||||
email: {
|
email: {
|
||||||
start: () => pushModal(SignUpEmail, {next: flows.email.profile}),
|
start: () => pushModal(SignUpEmail, {next: flows.email.profile, step: 1, totalSteps: 3}),
|
||||||
profile: () => pushModal(SignUpProfile, {next: flows.email.complete}),
|
profile: () => pushModal(SignUpProfile, {next: flows.email.complete, step: 2, totalSteps: 3}),
|
||||||
complete: () => pushModal(SignUpComplete, {next: flows.email.finalize}),
|
complete: () =>
|
||||||
|
pushModal(SignUpComplete, {next: flows.email.finalize, step: 3, totalSteps: 3}),
|
||||||
finalize: () => {
|
finalize: () => {
|
||||||
const email = getKey<string>("signup.email")!
|
const email = getKey<string>("signup.email")!
|
||||||
const clientOptions = getKey<ClientOptions>("signup.clientOptions")!
|
const clientOptions = getKey<ClientOptions>("signup.clientOptions")!
|
||||||
@@ -74,9 +75,10 @@
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
nostr: {
|
nostr: {
|
||||||
start: () => pushModal(SignUpProfile, {next: flows.nostr.key}),
|
start: () => pushModal(SignUpProfile, {next: flows.nostr.key, step: 1, totalSteps: 3}),
|
||||||
key: () => pushModal(SignUpKey, {next: flows.nostr.complete}),
|
key: () => pushModal(SignUpKey, {next: flows.nostr.complete, step: 2, totalSteps: 3}),
|
||||||
complete: () => pushModal(SignUpComplete, {next: flows.nostr.finalize}),
|
complete: () =>
|
||||||
|
pushModal(SignUpComplete, {next: flows.nostr.finalize, step: 3, totalSteps: 3}),
|
||||||
finalize: () => {
|
finalize: () => {
|
||||||
const secret = getKey<string>("signup.secret")!
|
const secret = getKey<string>("signup.secret")!
|
||||||
|
|
||||||
|
|||||||
@@ -9,12 +9,15 @@
|
|||||||
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
import ModalHeader from "@lib/components/ModalHeader.svelte"
|
||||||
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
|
import ProgressBar from "@app/components/ProgressBar.svelte"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
next: () => void
|
next: () => void
|
||||||
|
step?: number
|
||||||
|
totalSteps?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const {next}: Props = $props()
|
const {next, step, totalSteps}: Props = $props()
|
||||||
|
|
||||||
const back = () => history.back()
|
const back = () => history.back()
|
||||||
</script>
|
</script>
|
||||||
@@ -33,6 +36,9 @@
|
|||||||
on groups you've already joined. Click below to get started!
|
on groups you've already joined. Click below to get started!
|
||||||
</p>
|
</p>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
{#if step && totalSteps}
|
||||||
|
<ProgressBar current={step} total={totalSteps} />
|
||||||
|
{/if}
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button class="btn btn-link" onclick={back}>
|
<Button class="btn btn-link" onclick={back}>
|
||||||
<Icon icon={AltArrowLeft} />
|
<Icon icon={AltArrowLeft} />
|
||||||
|
|||||||
@@ -18,14 +18,17 @@
|
|||||||
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
import SignUpEmailConfirm from "@app/components/SignUpEmailConfirm.svelte"
|
import SignUpEmailConfirm from "@app/components/SignUpEmailConfirm.svelte"
|
||||||
|
import ProgressBar from "@app/components/ProgressBar.svelte"
|
||||||
import {pushToast, popToast} from "@app/util/toast"
|
import {pushToast, popToast} from "@app/util/toast"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
next: () => void
|
next: () => void
|
||||||
|
step?: number
|
||||||
|
totalSteps?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const {next}: Props = $props()
|
const {next, step, totalSteps}: Props = $props()
|
||||||
|
|
||||||
const back = () => history.back()
|
const back = () => history.back()
|
||||||
|
|
||||||
@@ -81,7 +84,7 @@
|
|||||||
setKey("signup.clientOptions", clientOptions)
|
setKey("signup.clientOptions", clientOptions)
|
||||||
|
|
||||||
popToast(toastId)
|
popToast(toastId)
|
||||||
pushModal(SignUpEmailConfirm, {next})
|
pushModal(SignUpEmailConfirm, {next, step, totalSteps})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
console.error(e)
|
||||||
|
|
||||||
@@ -120,7 +123,7 @@
|
|||||||
{#snippet input()}
|
{#snippet input()}
|
||||||
<label class="input input-bordered flex w-full items-center gap-2">
|
<label class="input input-bordered flex w-full items-center gap-2">
|
||||||
<Icon icon={Letter} />
|
<Icon icon={Letter} />
|
||||||
<input bind:value={email} />
|
<input type="email" bind:value={email} />
|
||||||
</label>
|
</label>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</FieldInline>
|
</FieldInline>
|
||||||
@@ -134,8 +137,14 @@
|
|||||||
<input type="password" bind:value={password} />
|
<input type="password" bind:value={password} />
|
||||||
</label>
|
</label>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
{#snippet info()}
|
||||||
|
Must be at least 12 characters long.
|
||||||
|
{/snippet}
|
||||||
</FieldInline>
|
</FieldInline>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
{#if step && totalSteps}
|
||||||
|
<ProgressBar current={step} total={totalSteps} />
|
||||||
|
{/if}
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button class="btn btn-link" onclick={back}>
|
<Button class="btn btn-link" onclick={back}>
|
||||||
<Icon icon={AltArrowLeft} />
|
<Icon icon={AltArrowLeft} />
|
||||||
|
|||||||
@@ -15,12 +15,15 @@
|
|||||||
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
import ModalTitle from "@lib/components/ModalTitle.svelte"
|
||||||
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
import ModalSubtitle from "@lib/components/ModalSubtitle.svelte"
|
||||||
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
import ModalFooter from "@lib/components/ModalFooter.svelte"
|
||||||
|
import ProgressBar from "@app/components/ProgressBar.svelte"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
next: () => void
|
next: () => void
|
||||||
|
step?: number
|
||||||
|
totalSteps?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const {next}: Props = $props()
|
const {next, step, totalSteps}: Props = $props()
|
||||||
|
|
||||||
const email = getKey<string>("signup.email")
|
const email = getKey<string>("signup.email")
|
||||||
|
|
||||||
@@ -61,6 +64,9 @@
|
|||||||
above.
|
above.
|
||||||
</p>
|
</p>
|
||||||
</ModalBody>
|
</ModalBody>
|
||||||
|
{#if step && totalSteps}
|
||||||
|
<ProgressBar current={step} total={totalSteps} />
|
||||||
|
{/if}
|
||||||
<ModalFooter>
|
<ModalFooter>
|
||||||
<Button class="btn btn-link" onclick={back} disabled={loading}>
|
<Button class="btn btn-link" onclick={back} disabled={loading}>
|
||||||
<Icon icon={AltArrowLeft} />
|
<Icon icon={AltArrowLeft} />
|
||||||
|
|||||||
@@ -4,11 +4,13 @@
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
next: () => void
|
next: () => void
|
||||||
|
step?: number
|
||||||
|
totalSteps?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const {next}: Props = $props()
|
const {next, step, totalSteps}: Props = $props()
|
||||||
|
|
||||||
const secret = getKey<string>("signup.secret")!
|
const secret = getKey<string>("signup.secret")!
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<KeyDownload {secret} {next} />
|
<KeyDownload {secret} {next} {step} {totalSteps} />
|
||||||
|
|||||||
@@ -5,15 +5,16 @@
|
|||||||
import {getKey, setKey} from "@lib/implicit"
|
import {getKey, setKey} from "@lib/implicit"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import Modal from "@lib/components/Modal.svelte"
|
|
||||||
import ModalBody from "@lib/components/ModalBody.svelte"
|
|
||||||
import ProfileEditForm from "@app/components/ProfileEditForm.svelte"
|
import ProfileEditForm from "@app/components/ProfileEditForm.svelte"
|
||||||
|
import ProgressBar from "@app/components/ProgressBar.svelte"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
next: () => void
|
next: () => void
|
||||||
|
step?: number
|
||||||
|
totalSteps?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const {next}: Props = $props()
|
const {next, step, totalSteps}: Props = $props()
|
||||||
|
|
||||||
const profile = getKey<Profile>("signup.profile")!
|
const profile = getKey<Profile>("signup.profile")!
|
||||||
|
|
||||||
@@ -27,19 +28,20 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<Modal>
|
<ProfileEditForm isSignup {initialValues} {onsubmit}>
|
||||||
<ModalBody>
|
{#snippet footer()}
|
||||||
<ProfileEditForm isSignup {initialValues} {onsubmit}>
|
<Button class="btn btn-link" onclick={back}>
|
||||||
{#snippet footer()}
|
<Icon icon={AltArrowLeft} />
|
||||||
<Button class="btn btn-link" onclick={back}>
|
Go back
|
||||||
<Icon icon={AltArrowLeft} />
|
</Button>
|
||||||
Go back
|
<Button class="btn btn-primary" type="submit">
|
||||||
</Button>
|
Create Account
|
||||||
<Button class="btn btn-primary" type="submit">
|
<Icon icon={AltArrowRight} />
|
||||||
Create Account
|
</Button>
|
||||||
<Icon icon={AltArrowRight} />
|
{/snippet}
|
||||||
</Button>
|
{#snippet progressBar()}
|
||||||
{/snippet}
|
{#if step && totalSteps}
|
||||||
</ProfileEditForm>
|
<ProgressBar current={step} total={totalSteps} />
|
||||||
</ModalBody>
|
{/if}
|
||||||
</Modal>
|
{/snippet}
|
||||||
|
</ProfileEditForm>
|
||||||
|
|||||||
@@ -25,6 +25,16 @@
|
|||||||
const {url} = $props()
|
const {url} = $props()
|
||||||
|
|
||||||
const authError = deriveRelayAuthError(url)
|
const authError = deriveRelayAuthError(url)
|
||||||
|
let networkError = $state(false)
|
||||||
|
const isExplicitAuthError = $derived(
|
||||||
|
$authError &&
|
||||||
|
!(
|
||||||
|
$authError.toLowerCase().includes("failed") ||
|
||||||
|
$authError.toLowerCase().includes("timeout") ||
|
||||||
|
$authError.toLowerCase().includes("network")
|
||||||
|
),
|
||||||
|
)
|
||||||
|
const isGenericError = $derived(networkError || ($authError && !isExplicitAuthError))
|
||||||
|
|
||||||
const back = () => history.back()
|
const back = () => history.back()
|
||||||
const copyInvite = () => clip(invite)
|
const copyInvite = () => clip(invite)
|
||||||
@@ -70,8 +80,14 @@
|
|||||||
])
|
])
|
||||||
|
|
||||||
claim = getTagValue("claim", event?.tags || []) || ""
|
claim = getTagValue("claim", event?.tags || []) || ""
|
||||||
} catch {
|
} catch (err) {
|
||||||
claim = ""
|
claim = ""
|
||||||
|
if (
|
||||||
|
(err instanceof Error && (err.name === "AbortError" || err.name === "TimeoutError")) ||
|
||||||
|
!navigator.onLine
|
||||||
|
) {
|
||||||
|
networkError = true
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
@@ -92,7 +108,11 @@
|
|||||||
<p class="center">
|
<p class="center">
|
||||||
<Spinner {loading}>Requesting an invite link...</Spinner>
|
<Spinner {loading}>Requesting an invite link...</Spinner>
|
||||||
</p>
|
</p>
|
||||||
{:else if $authError}
|
{:else if isGenericError}
|
||||||
|
<p class="center text-center">
|
||||||
|
Unable to reach the relay. Please check your connection and try again.
|
||||||
|
</p>
|
||||||
|
{:else if isExplicitAuthError}
|
||||||
<p class="center">Oops! It looks like you're not a member of this relay.</p>
|
<p class="center">Oops! It looks like you're not a member of this relay.</p>
|
||||||
{:else}
|
{:else}
|
||||||
<div class="flex flex-col items-center gap-6">
|
<div class="flex flex-col items-center gap-6">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {derived} from "svelte/store"
|
import {derived} from "svelte/store"
|
||||||
import {displayRelayUrl, EVENT_TIME, ZAP_GOAL, THREAD, CLASSIFIED, POLL} from "@welshman/util"
|
import {displayRelayUrl, EVENT_TIME, ZAP_GOAL, THREAD, CLASSIFIED, POLL} from "@welshman/util"
|
||||||
import {deriveRelay, deriveRelayDisplay, createSearch, pubkey} from "@welshman/app"
|
import {deriveRelay, createSearch, pubkey} from "@welshman/app"
|
||||||
import {fly} from "@lib/transition"
|
import {fly} from "@lib/transition"
|
||||||
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
|
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
|
||||||
import AltArrowDown from "@assets/icons/alt-arrow-down.svg?dataurl"
|
import AltArrowDown from "@assets/icons/alt-arrow-down.svg?dataurl"
|
||||||
@@ -65,7 +65,6 @@
|
|||||||
const {url} = $props()
|
const {url} = $props()
|
||||||
|
|
||||||
const relay = deriveRelay(url)
|
const relay = deriveRelay(url)
|
||||||
const display = deriveRelayDisplay(url)
|
|
||||||
const chatPath = makeSpacePath(url, "chat")
|
const chatPath = makeSpacePath(url, "chat")
|
||||||
const goalsPath = makeSpacePath(url, "goals")
|
const goalsPath = makeSpacePath(url, "goals")
|
||||||
const threadsPath = makeSpacePath(url, "threads")
|
const threadsPath = makeSpacePath(url, "threads")
|
||||||
@@ -144,9 +143,7 @@
|
|||||||
class="relative flex w-full flex-col rounded-xl p-3 transition-all hover:bg-base-100"
|
class="relative flex w-full flex-col rounded-xl p-3 transition-all hover:bg-base-100"
|
||||||
onclick={openMenu}>
|
onclick={openMenu}>
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<strong
|
<strong class="flex items-center gap-1 relative">
|
||||||
class="flex items-center gap-1 relative tooltip tooltip-right"
|
|
||||||
data-tip={$display}>
|
|
||||||
<RelayName {url} class="ellipsize" />
|
<RelayName {url} class="ellipsize" />
|
||||||
<div
|
<div
|
||||||
class="absolute -right-3 top-0 h-2 w-2 rounded-full bg-primary transition-all opacity-0"
|
class="absolute -right-3 top-0 h-2 w-2 rounded-full bg-primary transition-all opacity-0"
|
||||||
|
|||||||
@@ -26,27 +26,27 @@
|
|||||||
{@const {pubkey, software, version, supported_nips, limitation} = $relay}
|
{@const {pubkey, software, version, supported_nips, limitation} = $relay}
|
||||||
<div class="flex flex-wrap gap-1">
|
<div class="flex flex-wrap gap-1">
|
||||||
{#if pubkey}
|
{#if pubkey}
|
||||||
<div class="badge badge-neutral">
|
<div class="badge badge-neutral text-wrap h-auto">
|
||||||
<span class="ellipsize">Administrator: <ProfileLink unstyled {pubkey} /></span>
|
<span class="ellipsize">Administrator: <ProfileLink unstyled {pubkey} /></span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if $relay?.contact}
|
{#if $relay?.contact}
|
||||||
<div class="badge badge-neutral">
|
<div class="badge badge-neutral text-wrap h-auto">
|
||||||
<span class="ellipsize">Contact: {$relay.contact}</span>
|
<span class="ellipsize">Contact: {$relay.contact}</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if software}
|
{#if software}
|
||||||
<div class="badge badge-neutral">
|
<div class="badge badge-neutral text-wrap h-auto">
|
||||||
<span class="ellipsize">Software: {software}</span>
|
<span class="ellipsize">Software: {software}</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if version}
|
{#if version}
|
||||||
<div class="badge badge-neutral">
|
<div class="badge badge-neutral text-wrap h-auto">
|
||||||
<span class="ellipsize">Version: {version}</span>
|
<span class="ellipsize">Version: {version}</span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if Array.isArray(supported_nips)}
|
{#if Array.isArray(supported_nips)}
|
||||||
<p class="badge badge-neutral">
|
<p class="badge badge-neutral text-wrap h-auto">
|
||||||
<span class="ellipsize">Supported NIPs: {supported_nips.join(", ")}</span>
|
<span class="ellipsize">Supported NIPs: {supported_nips.join(", ")}</span>
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -61,7 +61,7 @@
|
|||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
{#if limitation?.min_pow_difficulty}
|
{#if limitation?.min_pow_difficulty}
|
||||||
<p class="badge badge-warning">
|
<p class="badge badge-warning text-wrap h-auto">
|
||||||
<span class="ellipsize">Min PoW: {limitation?.min_pow_difficulty}</span>
|
<span class="ellipsize">Min PoW: {limitation?.min_pow_difficulty}</span>
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -2,9 +2,10 @@
|
|||||||
import {tick} from "svelte"
|
import {tick} from "svelte"
|
||||||
import {debounce} from "throttle-debounce"
|
import {debounce} from "throttle-debounce"
|
||||||
import {request} from "@welshman/net"
|
import {request} from "@welshman/net"
|
||||||
import {formatTimestampAsDate, groupBy, now, MINUTE, HOUR, DAY, WEEK} from "@welshman/lib"
|
import {repository, tracker} from "@welshman/app"
|
||||||
|
import {formatTimestampAsDate, groupBy, uniqBy, now, MINUTE, HOUR, DAY, WEEK} from "@welshman/lib"
|
||||||
import type {TrustedEvent, Filter} from "@welshman/util"
|
import type {TrustedEvent, Filter} from "@welshman/util"
|
||||||
import {sortEventsDesc} from "@welshman/util"
|
import {MESSAGE, sortEventsDesc} from "@welshman/util"
|
||||||
import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
|
import CloseCircle from "@assets/icons/close-circle.svg?dataurl"
|
||||||
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
|
import Magnifier from "@assets/icons/magnifier.svg?dataurl"
|
||||||
import {fly} from "@lib/transition"
|
import {fly} from "@lib/transition"
|
||||||
@@ -53,8 +54,11 @@
|
|||||||
|
|
||||||
const getFilter = (searchTerm: string): Filter =>
|
const getFilter = (searchTerm: string): Filter =>
|
||||||
h
|
h
|
||||||
? {kinds: CONTENT_KINDS, "#h": [h], search: searchTerm}
|
? {kinds: [MESSAGE, ...CONTENT_KINDS], "#h": [h], search: searchTerm}
|
||||||
: {kinds: CONTENT_KINDS, search: searchTerm}
|
: {kinds: [MESSAGE, ...CONTENT_KINDS], search: searchTerm}
|
||||||
|
|
||||||
|
const getLocalResults = (filter: Filter) =>
|
||||||
|
repository.query([filter]).filter(event => tracker.getRelays(event.id).has(url))
|
||||||
|
|
||||||
const search = debounce(300, async (searchTerm: string) => {
|
const search = debounce(300, async (searchTerm: string) => {
|
||||||
controller?.abort()
|
controller?.abort()
|
||||||
@@ -68,18 +72,23 @@
|
|||||||
controller = new AbortController()
|
controller = new AbortController()
|
||||||
loading = true
|
loading = true
|
||||||
|
|
||||||
|
const filter = getFilter(searchTerm.trim())
|
||||||
|
const localResults = getLocalResults(filter)
|
||||||
|
|
||||||
|
results = sortEventsDesc(localResults)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const events = await request({
|
const events = await request({
|
||||||
relays: getRelayUrls(),
|
relays: getRelayUrls(),
|
||||||
autoClose: true,
|
autoClose: true,
|
||||||
signal: controller.signal,
|
signal: controller.signal,
|
||||||
filters: [getFilter(searchTerm.trim())],
|
filters: [filter],
|
||||||
})
|
})
|
||||||
|
|
||||||
results = sortEventsDesc(events)
|
results = sortEventsDesc(uniqBy((e: TrustedEvent) => e.id, [...events, ...localResults]))
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (!(error instanceof DOMException && error.name === "AbortError")) {
|
if (!(error instanceof DOMException && error.name === "AbortError")) {
|
||||||
results = []
|
results = sortEventsDesc(localResults)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
import ProfileCircle from "@app/components/ProfileCircle.svelte"
|
||||||
import VideoCallTile from "@app/components/VideoCallTile.svelte"
|
import VideoCallTile from "@app/components/VideoCallTile.svelte"
|
||||||
import VoiceWidget from "@app/components/VoiceWidget.svelte"
|
import VoiceWidget from "@app/components/VoiceWidget.svelte"
|
||||||
|
import VoiceParticipantMediaBadges from "@app/components/VoiceParticipantMediaBadges.svelte"
|
||||||
import {get} from "svelte/store"
|
import {get} from "svelte/store"
|
||||||
import {
|
import {
|
||||||
VideoCallLayout,
|
VideoCallLayout,
|
||||||
@@ -18,7 +19,12 @@
|
|||||||
ViewportSize,
|
ViewportSize,
|
||||||
videoPrimaryTileKey,
|
videoPrimaryTileKey,
|
||||||
} from "@app/call/video"
|
} from "@app/call/video"
|
||||||
import {currentVoiceSession, currentVoiceRoom, pubkeyFromLiveKitIdentity} from "@app/call/stores"
|
import {
|
||||||
|
currentVoiceSession,
|
||||||
|
currentVoiceRoom,
|
||||||
|
mediaStateByIdentity,
|
||||||
|
pubkeyFromLiveKitIdentity,
|
||||||
|
} from "@app/call/stores"
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
layout: VideoCallLayout
|
layout: VideoCallLayout
|
||||||
@@ -123,6 +129,28 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const tiledIdentities = new Set(videoTiles.map(t => t.identity))
|
||||||
|
if (!tiledIdentities.has(user.identity)) {
|
||||||
|
videoTiles.push({
|
||||||
|
identity: user.identity,
|
||||||
|
isLocal: true,
|
||||||
|
trackSid: "local-avatar",
|
||||||
|
track: undefined,
|
||||||
|
source: Track.Source.Camera,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
for (const rp of room.remoteParticipants.values()) {
|
||||||
|
if (!tiledIdentities.has(rp.identity)) {
|
||||||
|
videoTiles.push({
|
||||||
|
identity: rp.identity,
|
||||||
|
isLocal: false,
|
||||||
|
trackSid: `avatar-${rp.identity}`,
|
||||||
|
track: undefined,
|
||||||
|
source: Track.Source.Camera,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return videoTiles
|
return videoTiles
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -184,6 +212,7 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet videoTile(tile: VideoTileData, layout: TileLayout)}
|
{#snippet videoTile(tile: VideoTileData, layout: TileLayout)}
|
||||||
|
{@const media = $mediaStateByIdentity(tile.identity)}
|
||||||
<div
|
<div
|
||||||
class={cx(
|
class={cx(
|
||||||
"relative isolate overflow-hidden rounded-box shadow-sm",
|
"relative isolate overflow-hidden rounded-box shadow-sm",
|
||||||
@@ -203,6 +232,15 @@
|
|||||||
<ProfileCircle pubkey={pubkeyFromLiveKitIdentity(tile.identity)} {url} size={14} />
|
<ProfileCircle pubkey={pubkeyFromLiveKitIdentity(tile.identity)} {url} size={14} />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
{#if tile.track}
|
||||||
|
<div class="pointer-events-none absolute left-1 top-1 z-10">
|
||||||
|
<VoiceParticipantMediaBadges
|
||||||
|
muted={media.muted}
|
||||||
|
cameraOn={media.cameraOn}
|
||||||
|
showCamera={tile.source === Track.Source.Camera}
|
||||||
|
size={3} />
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
<span
|
<span
|
||||||
class="pointer-events-none absolute bottom-1 left-1 max-w-[calc(100%-0.5rem)] truncate rounded bg-base-100/80 px-1.5 py-0.5 text-xs">
|
class="pointer-events-none absolute bottom-1 left-1 max-w-[calc(100%-0.5rem)] truncate rounded bg-base-100/80 px-1.5 py-0.5 text-xs">
|
||||||
{labelFor(tile.identity, tile.source)}{tile.isLocal ? " (you)" : ""}
|
{labelFor(tile.identity, tile.source)}{tile.isLocal ? " (you)" : ""}
|
||||||
@@ -254,8 +292,10 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<div
|
<div
|
||||||
class="flex min-h-[12rem] flex-1 flex-col items-center justify-center gap-2 rounded-box bg-base-200/50 p-4 text-center text-sm opacity-80">
|
class="flex min-h-[12rem] flex-1 flex-col items-center justify-center gap-2 rounded-box bg-base-200/50 p-4 text-center text-sm opacity-80">
|
||||||
<p>No camera or screen share yet.</p>
|
<p>No one is sharing video yet.</p>
|
||||||
<p class="text-xs">Use the camera or screen share control to share video.</p>
|
<p class="text-xs">
|
||||||
|
Participants appear here when they turn on their camera or share their screen.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import cx from "classnames"
|
||||||
|
import MicrophoneOff from "@assets/icons/microphone-off.svg?dataurl"
|
||||||
|
import VideocameraOff from "@assets/icons/videocamera-off.svg?dataurl"
|
||||||
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
muted: boolean
|
||||||
|
cameraOn: boolean
|
||||||
|
showCamera?: boolean
|
||||||
|
size?: number
|
||||||
|
class?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const {muted, cameraOn, showCamera = true, size = 3, class: className = ""}: Props = $props()
|
||||||
|
|
||||||
|
const badgeClass =
|
||||||
|
"inline-flex size-4 shrink-0 items-center justify-center rounded bg-base-100/80 p-0.5 text-error"
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if muted || (showCamera && !cameraOn)}
|
||||||
|
<div class={cx("flex items-center gap-1", className)}>
|
||||||
|
{#if muted}
|
||||||
|
<span class={badgeClass} aria-label="Muted">
|
||||||
|
<Icon icon={MicrophoneOff} {size} />
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
{#if showCamera && !cameraOn}
|
||||||
|
<span class={badgeClass} aria-label="Camera off">
|
||||||
|
<Icon icon={VideocameraOff} {size} />
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
@@ -9,11 +9,13 @@
|
|||||||
import {makeRoomPath} from "@app/util/routes"
|
import {makeRoomPath} from "@app/util/routes"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
import VoiceRoomJoinDialog from "@app/components/VoiceRoomJoinDialog.svelte"
|
import VoiceRoomJoinDialog from "@app/components/VoiceRoomJoinDialog.svelte"
|
||||||
|
import VoiceParticipantMediaBadges from "@app/components/VoiceParticipantMediaBadges.svelte"
|
||||||
import {makeRoomId} from "@app/core/state"
|
import {makeRoomId} from "@app/core/state"
|
||||||
import {
|
import {
|
||||||
VoiceState,
|
VoiceState,
|
||||||
currentVoiceRoom,
|
currentVoiceRoom,
|
||||||
isParticipantSpeaking,
|
isParticipantSpeaking,
|
||||||
|
mediaStateByIdentity,
|
||||||
participantKey,
|
participantKey,
|
||||||
voiceState,
|
voiceState,
|
||||||
type VoiceParticipant,
|
type VoiceParticipant,
|
||||||
@@ -83,9 +85,17 @@
|
|||||||
)}>
|
)}>
|
||||||
<ProfileCircle pubkey={p.pubkey} size={5} class="h-5 w-5" />
|
<ProfileCircle pubkey={p.pubkey} size={5} class="h-5 w-5" />
|
||||||
</div>
|
</div>
|
||||||
<span class="ellipsize text-xs opacity-70">
|
<span class="ellipsize min-w-0 flex-1 text-xs opacity-70">
|
||||||
{p.pubkey ? displayProfileByPubkey(p.pubkey) : "Unknown"}
|
{p.pubkey ? displayProfileByPubkey(p.pubkey) : "Unknown"}
|
||||||
</span>
|
</span>
|
||||||
|
{#if isActive}
|
||||||
|
{@const media = $mediaStateByIdentity(p.identity)}
|
||||||
|
<VoiceParticipantMediaBadges
|
||||||
|
muted={media.muted}
|
||||||
|
cameraOn={media.cameraOn}
|
||||||
|
size={3}
|
||||||
|
class="shrink-0" />
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
import {displayRelayUrl} from "@welshman/util"
|
import {displayRelayUrl} from "@welshman/util"
|
||||||
import Microphone from "@assets/icons/microphone.svg?dataurl"
|
import Microphone from "@assets/icons/microphone.svg?dataurl"
|
||||||
import Videocamera from "@assets/icons/videocamera.svg?dataurl"
|
import MicrophoneOff from "@assets/icons/microphone-off.svg?dataurl"
|
||||||
|
import VideocameraOff from "@assets/icons/videocamera-off.svg?dataurl"
|
||||||
import VideocameraRecord from "@assets/icons/videocamera-record.svg?dataurl"
|
import VideocameraRecord from "@assets/icons/videocamera-record.svg?dataurl"
|
||||||
import Monitor from "@assets/icons/monitor.svg?dataurl"
|
import Monitor from "@assets/icons/monitor.svg?dataurl"
|
||||||
import PhoneRounded from "@assets/icons/phone-rounded.svg?dataurl"
|
import PhoneRounded from "@assets/icons/phone-rounded.svg?dataurl"
|
||||||
@@ -37,13 +38,7 @@
|
|||||||
toggleScreenShare,
|
toggleScreenShare,
|
||||||
videoCallLayout,
|
videoCallLayout,
|
||||||
} from "@app/call/video"
|
} from "@app/call/video"
|
||||||
import {
|
import {VoiceState, currentVoiceSession, currentVoiceRoom, voiceState} from "@app/call/stores"
|
||||||
VoiceState,
|
|
||||||
currentVoiceSession,
|
|
||||||
currentVoiceRoom,
|
|
||||||
voiceState,
|
|
||||||
isLocalSpeaking,
|
|
||||||
} from "@app/call/stores"
|
|
||||||
import {cancelJoinVoiceRoom, leaveVoiceRoom, toggleMute} from "@app/call/voice"
|
import {cancelJoinVoiceRoom, leaveVoiceRoom, toggleMute} from "@app/call/voice"
|
||||||
|
|
||||||
const {relay, h} = $derived($page.params)
|
const {relay, h} = $derived($page.params)
|
||||||
@@ -187,29 +182,26 @@
|
|||||||
class={cx(
|
class={cx(
|
||||||
mediaToggleClass,
|
mediaToggleClass,
|
||||||
"overflow-visible",
|
"overflow-visible",
|
||||||
!$currentVoiceSession.muted && $isLocalSpeaking && "text-primary",
|
!$currentVoiceSession.muted && "text-primary",
|
||||||
$currentVoiceSession.muted &&
|
$currentVoiceSession.muted &&
|
||||||
"text-error ring-1 ring-error/50 ring-offset-0 ring-offset-base-100",
|
"text-error ring-1 ring-error/50 ring-offset-0 ring-offset-base-100",
|
||||||
)}
|
)}
|
||||||
onclick={toggleMute}>
|
onclick={toggleMute}>
|
||||||
<span class="relative inline-flex items-center justify-center overflow-visible">
|
<Icon icon={$currentVoiceSession.muted ? MicrophoneOff : Microphone} size={4} />
|
||||||
<Icon icon={Microphone} size={4} />
|
|
||||||
{#if $currentVoiceSession.muted}
|
|
||||||
<span
|
|
||||||
class="pointer-events-none absolute inset-0 flex items-center justify-center overflow-visible"
|
|
||||||
aria-hidden="true">
|
|
||||||
<span
|
|
||||||
class="h-[1.3px] w-[150%] max-w-none shrink-0 -rotate-45 rounded-full bg-current"
|
|
||||||
></span>
|
|
||||||
</span>
|
|
||||||
{/if}
|
|
||||||
</span>
|
|
||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
data-tip={$currentVoiceSession.cameraOn ? "Turn off camera" : "Turn on camera"}
|
data-tip={$currentVoiceSession.cameraOn ? "Turn off camera" : "Turn on camera"}
|
||||||
class={cx(mediaToggleClass, $currentVoiceSession.cameraOn && "text-primary")}
|
class={cx(
|
||||||
|
mediaToggleClass,
|
||||||
|
"overflow-visible",
|
||||||
|
$currentVoiceSession.cameraOn && "text-primary",
|
||||||
|
!$currentVoiceSession.cameraOn &&
|
||||||
|
"text-error ring-1 ring-error/50 ring-offset-0 ring-offset-base-100",
|
||||||
|
)}
|
||||||
onclick={toggleCamera}>
|
onclick={toggleCamera}>
|
||||||
<Icon icon={$currentVoiceSession.cameraOn ? VideocameraRecord : Videocamera} size={4} />
|
<Icon
|
||||||
|
icon={$currentVoiceSession.cameraOn ? VideocameraRecord : VideocameraOff}
|
||||||
|
size={4} />
|
||||||
</Button>
|
</Button>
|
||||||
{#if !Capacitor.isNativePlatform()}
|
{#if !Capacitor.isNativePlatform()}
|
||||||
<Button
|
<Button
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ import {
|
|||||||
waitForThunkError,
|
waitForThunkError,
|
||||||
getPubkeyRelays,
|
getPubkeyRelays,
|
||||||
userBlossomServerList,
|
userBlossomServerList,
|
||||||
getThunkError,
|
|
||||||
addRoomMember,
|
addRoomMember,
|
||||||
manageRelay,
|
manageRelay,
|
||||||
getRelay,
|
getRelay,
|
||||||
@@ -264,16 +263,12 @@ export const attemptRelayAccess = async (url: string, claim = "") => {
|
|||||||
return stripPrefix(error)
|
return stripPrefix(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deriveRelayAuthError = (url: string, claim = "") => {
|
export const deriveRelayAuthError = (url: string) => {
|
||||||
// Kick off the auth process
|
|
||||||
Pool.get().get(url).auth.attemptAuth(sign)
|
Pool.get().get(url).auth.attemptAuth(sign)
|
||||||
|
|
||||||
// Attempt to join the relay
|
|
||||||
const thunk = publishJoinRequest({url, claim})
|
|
||||||
|
|
||||||
return derived(
|
return derived(
|
||||||
[thunk, relaysMostlyRestricted, deriveSocket(url)],
|
[relaysMostlyRestricted, deriveSocket(url)],
|
||||||
([$thunk, $relaysMostlyRestricted, $socket]) => {
|
([$relaysMostlyRestricted, $socket]) => {
|
||||||
if ($socket.auth.status === AuthStatus.Forbidden && $socket.auth.details) {
|
if ($socket.auth.status === AuthStatus.Forbidden && $socket.auth.details) {
|
||||||
return stripPrefix($socket.auth.details)
|
return stripPrefix($socket.auth.details)
|
||||||
}
|
}
|
||||||
@@ -281,16 +276,6 @@ export const deriveRelayAuthError = (url: string, claim = "") => {
|
|||||||
if ($relaysMostlyRestricted[url]) {
|
if ($relaysMostlyRestricted[url]) {
|
||||||
return stripPrefix($relaysMostlyRestricted[url])
|
return stripPrefix($relaysMostlyRestricted[url])
|
||||||
}
|
}
|
||||||
|
|
||||||
const error = getThunkError($thunk)
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
const isEmptyInvite = !claim && error.includes("invite code")
|
|
||||||
|
|
||||||
if (!shouldIgnoreError(error) && !isEmptyInvite) {
|
|
||||||
return stripPrefix(error) || "join request rejected"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -560,11 +560,7 @@ export const chatsById = call(() => {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
export const deriveChat = call(() => {
|
export const deriveChat = makeDeriveItem(chatsById)
|
||||||
const _deriveChat = makeDeriveItem(chatsById)
|
|
||||||
|
|
||||||
return (pubkeys: string[]) => _deriveChat(makeChatId(pubkeys))
|
|
||||||
})
|
|
||||||
|
|
||||||
export const chatSearch = derived(throttled(1500, chatsById), $chatsByPubkey => {
|
export const chatSearch = derived(throttled(1500, chatsById), $chatsByPubkey => {
|
||||||
return createSearch(
|
return createSearch(
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import {App} from "@capacitor/app"
|
||||||
import {Capacitor} from "@capacitor/core"
|
import {Capacitor} from "@capacitor/core"
|
||||||
import {Keyboard} from "@capacitor/keyboard"
|
import {Keyboard} from "@capacitor/keyboard"
|
||||||
import {noop} from "@welshman/lib"
|
import {noop} from "@welshman/lib"
|
||||||
@@ -13,9 +14,16 @@ export const syncKeyboard = () => {
|
|||||||
document.body.classList.remove("keyboard-open")
|
document.body.classList.remove("keyboard-open")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// On Android, system-dismissing the IME during pause doesn't fire keyboardWillHide,
|
||||||
|
// so on resume we force a hide to re-sync native insets and clear our CSS state.
|
||||||
|
const resumeListener = App.addListener("appStateChange", ({isActive}) => {
|
||||||
|
if (isActive) Keyboard.hide()
|
||||||
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
showListener.then(listener => listener.remove())
|
showListener.then(listener => listener.remove())
|
||||||
hideListener.then(listener => listener.remove())
|
hideListener.then(listener => listener.remove())
|
||||||
|
resumeListener.then(listener => listener.remove())
|
||||||
document.body.classList.remove("keyboard-open")
|
document.body.classList.remove("keyboard-open")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import {derived} from "svelte/store"
|
import {derived, get, writable} from "svelte/store"
|
||||||
import {Badge} from "@capawesome/capacitor-badge"
|
import {Badge} from "@capawesome/capacitor-badge"
|
||||||
import {synced, throttled, withGetter} from "@welshman/store"
|
import {synced, throttled, withGetter} from "@welshman/store"
|
||||||
import {pubkey, tracker, repository, relaysByUrl} from "@welshman/app"
|
import {pubkey, tracker, repository, relaysByUrl} from "@welshman/app"
|
||||||
@@ -36,6 +36,9 @@ export const deriveChecked = (key: string) => derived(checked, prop<number>(key)
|
|||||||
|
|
||||||
export const setChecked = (key: string) => checked.update(assoc(key, now()))
|
export const setChecked = (key: string) => checked.update(assoc(key, now()))
|
||||||
|
|
||||||
|
/** Room path while video call UI hides chat; checked + badge stay active until chat is shown. */
|
||||||
|
export const deferredRoomPath = writable<string | undefined>(undefined)
|
||||||
|
|
||||||
export const syncChecked = () => {
|
export const syncChecked = () => {
|
||||||
let prev = ""
|
let prev = ""
|
||||||
|
|
||||||
@@ -57,8 +60,11 @@ export const syncChecked = () => {
|
|||||||
|
|
||||||
// Set checked when we visit a given page - but delay it a tad
|
// Set checked when we visit a given page - but delay it a tad
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
const defer = get(deferredRoomPath)
|
||||||
|
|
||||||
checked.update($checked => {
|
checked.update($checked => {
|
||||||
for (const path of getPaths($page.url.pathname)) {
|
for (const path of getPaths($page.url.pathname)) {
|
||||||
|
if (defer && path === defer) continue
|
||||||
$checked[path] = now()
|
$checked[path] = now()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,9 +157,17 @@ export const allNotifications = derived(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
export const notifications = derived([page, allNotifications], ([$page, $allNotifications]) => {
|
export const notifications = derived(
|
||||||
return new Set([...$allNotifications].filter(p => !$page.url.pathname.startsWith(p)))
|
[page, allNotifications, deferredRoomPath],
|
||||||
})
|
([$page, $allNotifications, $deferredRoomPath]) =>
|
||||||
|
new Set(
|
||||||
|
[...$allNotifications].filter(p => {
|
||||||
|
if (!$page.url.pathname.startsWith(p)) return true
|
||||||
|
if ($deferredRoomPath && p === $deferredRoomPath) return true
|
||||||
|
return false
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
// Badges
|
// Badges
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
export const POMADE_INVALID_LOGIN_MESSAGE = "Invalid login information"
|
||||||
|
export const POMADE_NETWORK_ERROR_MESSAGE = "Network error, please try again"
|
||||||
|
|
||||||
|
type PomadeMessage = {
|
||||||
|
res?: unknown
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getPomadeLoginFailureMessage = (messages: PomadeMessage[]) =>
|
||||||
|
messages.some(message => message.res !== undefined)
|
||||||
|
? POMADE_INVALID_LOGIN_MESSAGE
|
||||||
|
: POMADE_NETWORK_ERROR_MESSAGE
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M2 11.5C2 8.21252 2 6.56878 2.90796 5.46243C3.07418 5.25989 3.25989 5.07418 3.46243 4.90796C4.56878 4 6.21252 4 9.5 4C12.7875 4 14.4312 4 15.5376 4.90796C15.7401 5.07418 15.9258 5.25989 16.092 5.46243C17 6.56878 17 8.21252 17 11.5V12.5C17 15.7875 17 17.4312 16.092 18.5376C15.9258 18.7401 15.7401 18.9258 15.5376 19.092C14.4312 20 12.7875 20 9.5 20C6.21252 20 4.56878 20 3.46243 19.092C3.25989 18.9258 3.07418 18.7401 2.90796 18.5376C2 17.4312 2 15.7875 2 12.5V11.5Z" stroke="#000000" stroke-width="1.5"/>
|
||||||
|
<path d="M17 9.50019L17.6584 9.17101C19.6042 8.19807 20.5772 7.7116 21.2886 8.15127C22 8.59094 22 9.67872 22 11.8543V12.1461C22 14.3217 22 15.4094 21.2886 15.8491C20.5772 16.2888 19.6042 15.8023 17.6584 14.8294L17 14.5002V9.50019Z" stroke="#000000" stroke-width="1.5"/>
|
||||||
|
<path d="M22 2L2 22" stroke="#000000" stroke-width="1.5" stroke-linecap="round"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 970 B |
@@ -31,6 +31,7 @@
|
|||||||
<svelte:document onmousemove={onMouseMove} />
|
<svelte:document onmousemove={onMouseMove} />
|
||||||
|
|
||||||
<Tippy
|
<Tippy
|
||||||
|
class="flex"
|
||||||
bind:popover
|
bind:popover
|
||||||
component={EmojiPicker}
|
component={EmojiPicker}
|
||||||
props={{onClick}}
|
props={{onClick}}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import "emoji-picker-element"
|
import "emoji-picker-element"
|
||||||
|
import emojiDataUrl from "emoji-picker-element-data/en/emojibase/data.json?url"
|
||||||
import type {Emoji} from "emoji-picker-element/shared"
|
import type {Emoji} from "emoji-picker-element/shared"
|
||||||
import {onMount} from "svelte"
|
import {onMount} from "svelte"
|
||||||
|
|
||||||
@@ -26,4 +27,4 @@
|
|||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<emoji-picker bind:this={element} class="m-auto"></emoji-picker>
|
<emoji-picker bind:this={element} data-source={emojiDataUrl} class="m-auto"></emoji-picker>
|
||||||
|
|||||||
@@ -9,16 +9,22 @@
|
|||||||
const {...props}: Props = $props()
|
const {...props}: Props = $props()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-2 lg:gap-6 lg:grid-cols-3 {props.class}">
|
<div class="flex flex-col gap-2 {props.class}">
|
||||||
<label class="flex items-center gap-2 font-bold">
|
<div class="flex items-center justify-between w-full gap-2">
|
||||||
{@render props.label?.()}
|
{#if props.label}
|
||||||
</label>
|
<label class="flex items-center gap-2 min-w-[30%] max-w-[80%] md:max-w-none">
|
||||||
<div class="col-span-2 flex items-center gap-2">
|
{@render props.label()}
|
||||||
{@render props.input?.()}
|
</label>
|
||||||
</div>
|
|
||||||
<p class="flex-end text-sm opacity-50 lg:col-span-3">
|
|
||||||
{#if props.info}
|
|
||||||
{@render props.info?.()}
|
|
||||||
{/if}
|
{/if}
|
||||||
</p>
|
<div class="flex items-center gap-2 justify-end grow">
|
||||||
|
{#if props.input}
|
||||||
|
{@render props.input()}
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{#if props.info}
|
||||||
|
<p class="text-sm opacity-50">
|
||||||
|
{@render props.info()}
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,12 @@
|
|||||||
placeholder?: string
|
placeholder?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
let {value = $bindable(), addLabel, placeholder = "Enter text..."}: Props = $props()
|
let {
|
||||||
|
value = $bindable(),
|
||||||
|
addLabel,
|
||||||
|
placeholder = "Enter text...",
|
||||||
|
allowAdd = true,
|
||||||
|
}: Props & {allowAdd?: boolean} = $props()
|
||||||
let draggedIndex: number | null = $state(null)
|
let draggedIndex: number | null = $state(null)
|
||||||
|
|
||||||
const onChange = (newValue: string[]) => {
|
const onChange = (newValue: string[]) => {
|
||||||
@@ -72,12 +77,14 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{/each}
|
{/each}
|
||||||
<Button onclick={addItem} class="btn btn-link w-fit px-0">
|
{#if allowAdd}
|
||||||
<Icon icon={AddCircle} size={5} />
|
<Button onclick={addItem} class="btn btn-link w-fit px-0">
|
||||||
{#if addLabel}
|
<Icon icon={AddCircle} size={5} />
|
||||||
{@render addLabel?.()}
|
{#if addLabel}
|
||||||
{:else}
|
{@render addLabel?.()}
|
||||||
Add Item
|
{:else}
|
||||||
{/if}
|
Add Item
|
||||||
</Button>
|
{/if}
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
const className = $derived(
|
const className = $derived(
|
||||||
cx(
|
cx(
|
||||||
props.class,
|
props.class,
|
||||||
"scroll-container z-feature flex min-h-0 w-full min-w-0 flex-1 flex-col overflow-y-auto overflow-x-hidden",
|
"scroll-container z-feature flex min-h-0 w-full min-w-0 flex-col overflow-y-auto overflow-x-hidden pb-14 md:pb-0",
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@
|
|||||||
cx(
|
cx(
|
||||||
"flex h-full w-full cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-base-300",
|
"flex h-full w-full cursor-pointer items-center justify-center rounded-full transition-colors hover:bg-base-300",
|
||||||
restProps.class,
|
restProps.class,
|
||||||
|
{"bg-base-300 border border-solid border-base-content/20": active},
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
import {Badge} from "@capawesome/capacitor-badge"
|
import {Badge} from "@capawesome/capacitor-badge"
|
||||||
import Bell from "@assets/icons/bell.svg?dataurl"
|
import Bell from "@assets/icons/bell.svg?dataurl"
|
||||||
import {preventDefault} from "@lib/html"
|
import {preventDefault} from "@lib/html"
|
||||||
|
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||||
import Spinner from "@lib/components/Spinner.svelte"
|
import Spinner from "@lib/components/Spinner.svelte"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
@@ -63,40 +64,64 @@
|
|||||||
<!-- pass -->
|
<!-- pass -->
|
||||||
{:then { isSupported }}
|
{:then { isSupported }}
|
||||||
{#if isSupported}
|
{#if isSupported}
|
||||||
<div class="flex justify-between">
|
<FieldInline>
|
||||||
<p>Show badge for unread alerts</p>
|
{#snippet label()}
|
||||||
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.badge} />
|
<p>Show badge for unread alerts</p>
|
||||||
</div>
|
{/snippet}
|
||||||
|
{#snippet input()}
|
||||||
|
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.badge} />
|
||||||
|
{/snippet}
|
||||||
|
</FieldInline>
|
||||||
{/if}
|
{/if}
|
||||||
{/await}
|
{/await}
|
||||||
{#if !Capacitor.isNativePlatform()}
|
{#if !Capacitor.isNativePlatform()}
|
||||||
<div class="flex justify-between">
|
<FieldInline>
|
||||||
<p>Play sound for new activity</p>
|
{#snippet label()}
|
||||||
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.sound} />
|
<p>Play sound for new activity</p>
|
||||||
</div>
|
{/snippet}
|
||||||
|
{#snippet input()}
|
||||||
|
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.sound} />
|
||||||
|
{/snippet}
|
||||||
|
</FieldInline>
|
||||||
{/if}
|
{/if}
|
||||||
<div class="flex justify-between">
|
<FieldInline>
|
||||||
<p>Enable push notifications</p>
|
{#snippet label()}
|
||||||
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.push} />
|
<p>Enable push notifications</p>
|
||||||
</div>
|
{/snippet}
|
||||||
|
{#snippet input()}
|
||||||
|
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.push} />
|
||||||
|
{/snippet}
|
||||||
|
</FieldInline>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class={cx("card2 bg-alt col-4 shadow-md", {
|
class={cx("card2 bg-alt col-4 shadow-md", {
|
||||||
"pointer-events-none opacity-50": !settings.badge && !settings.sound && !settings.push,
|
"pointer-events-none opacity-50": !settings.badge && !settings.sound && !settings.push,
|
||||||
})}>
|
})}>
|
||||||
<strong class="text-lg">Alert Types</strong>
|
<strong class="text-lg">Alert Types</strong>
|
||||||
<div class="flex justify-between">
|
<FieldInline>
|
||||||
<p>Notify me about new activity</p>
|
{#snippet label()}
|
||||||
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.spaces} />
|
<p>Notify me about new activity</p>
|
||||||
</div>
|
{/snippet}
|
||||||
<div class="flex justify-between">
|
{#snippet input()}
|
||||||
<p>Always notify me when mentioned</p>
|
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.spaces} />
|
||||||
<input type="checkbox" class="toggle toggle-primary" checked={settings.mentions} />
|
{/snippet}
|
||||||
</div>
|
</FieldInline>
|
||||||
<div class="flex justify-between">
|
<FieldInline>
|
||||||
<p>Notify me about new messages</p>
|
{#snippet label()}
|
||||||
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.messages} />
|
<p>Always notify me when mentioned</p>
|
||||||
</div>
|
{/snippet}
|
||||||
|
{#snippet input()}
|
||||||
|
<input type="checkbox" class="toggle toggle-primary" checked={settings.mentions} />
|
||||||
|
{/snippet}
|
||||||
|
</FieldInline>
|
||||||
|
<FieldInline>
|
||||||
|
{#snippet label()}
|
||||||
|
<p>Notify me about new messages</p>
|
||||||
|
{/snippet}
|
||||||
|
{#snippet input()}
|
||||||
|
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.messages} />
|
||||||
|
{/snippet}
|
||||||
|
</FieldInline>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="card2 bg-alt sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4">
|
class="card2 bg-alt sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4">
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
import {Router} from "@welshman/router"
|
import {Router} from "@welshman/router"
|
||||||
import {userMuteList, tagPubkey, publishThunk, userBlossomServerList} from "@welshman/app"
|
import {userMuteList, tagPubkey, publishThunk, userBlossomServerList} from "@welshman/app"
|
||||||
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
|
import NotesMinimalistic from "@assets/icons/notes-minimalistic.svg?dataurl"
|
||||||
|
import AddCircle from "@assets/icons/add-circle.svg?dataurl"
|
||||||
import {preventDefault} from "@lib/html"
|
import {preventDefault} from "@lib/html"
|
||||||
import Field from "@lib/components/Field.svelte"
|
import Field from "@lib/components/Field.svelte"
|
||||||
import FieldInline from "@lib/components/FieldInline.svelte"
|
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||||
@@ -28,6 +29,10 @@
|
|||||||
blossomServers = getTagValues("server", getListTags($userBlossomServerList))
|
blossomServers = getTagValues("server", getListTags($userBlossomServerList))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const addServer = () => {
|
||||||
|
blossomServers = [...blossomServers, ""]
|
||||||
|
}
|
||||||
|
|
||||||
const onsubmit = preventDefault(async () => {
|
const onsubmit = preventDefault(async () => {
|
||||||
await publishSettings($state.snapshot(settings))
|
await publishSettings($state.snapshot(settings))
|
||||||
|
|
||||||
@@ -104,7 +109,7 @@
|
|||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet input()}
|
{#snippet input()}
|
||||||
<input
|
<input
|
||||||
class="range range-primary"
|
class="range range-primary w-full"
|
||||||
type="range"
|
type="range"
|
||||||
min="0.8"
|
min="0.8"
|
||||||
max="1.3"
|
max="1.3"
|
||||||
@@ -115,13 +120,13 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="card2 bg-alt col-4 shadow-md">
|
<div class="card2 bg-alt col-4 shadow-md">
|
||||||
<strong class="text-lg">Editor Settings</strong>
|
<strong class="text-lg">Editor Settings</strong>
|
||||||
<FieldInline>
|
<Field>
|
||||||
{#snippet label()}
|
{#snippet label()}
|
||||||
<p>Send Delay</p>
|
<p>Send Delay</p>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet input()}
|
{#snippet input()}
|
||||||
<input
|
<input
|
||||||
class="range range-primary"
|
class="range range-primary w-full"
|
||||||
type="range"
|
type="range"
|
||||||
min="0"
|
min="0"
|
||||||
max="10000"
|
max="10000"
|
||||||
@@ -134,17 +139,19 @@
|
|||||||
{settings.send_delay === 1000 ? "second" : "seconds"}.
|
{settings.send_delay === 1000 ? "second" : "seconds"}.
|
||||||
</p>
|
</p>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</FieldInline>
|
</Field>
|
||||||
<Field>
|
<Field>
|
||||||
{#snippet label()}
|
{#snippet label()}
|
||||||
<p>Media Server</p>
|
<p>Media Server</p>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
{#snippet secondary()}
|
||||||
|
<Button class="link text-sm underline flex items-center gap-1" onclick={addServer}>
|
||||||
|
<Icon icon={AddCircle} size={4} />
|
||||||
|
Add Server
|
||||||
|
</Button>
|
||||||
|
{/snippet}
|
||||||
{#snippet input()}
|
{#snippet input()}
|
||||||
<InputList bind:value={blossomServers}>
|
<InputList allowAdd={false} bind:value={blossomServers} />
|
||||||
{#snippet addLabel()}
|
|
||||||
Add Server
|
|
||||||
{/snippet}
|
|
||||||
</InputList>
|
|
||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet info()}
|
{#snippet info()}
|
||||||
<p>Choose a media server type and url for files you upload to {PLATFORM_NAME}.</p>
|
<p>Choose a media server type and url for files you upload to {PLATFORM_NAME}.</p>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import ShieldMinimalistic from "@assets/icons/shield-minimalistic.svg?dataurl"
|
import ShieldMinimalistic from "@assets/icons/shield-minimalistic.svg?dataurl"
|
||||||
import {preventDefault} from "@lib/html"
|
import {preventDefault} from "@lib/html"
|
||||||
|
import FieldInline from "@lib/components/FieldInline.svelte"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
@@ -11,8 +12,10 @@
|
|||||||
settings = {...$userSettingsValues}
|
settings = {...$userSettingsValues}
|
||||||
}
|
}
|
||||||
|
|
||||||
const onAuthModeChange = (e: any) => {
|
const onAuthModeChange = (e: Event) => {
|
||||||
settings.auth_mode = e.target.checked ? RelayAuthMode.Aggressive : RelayAuthMode.Conservative
|
const target = e.currentTarget as HTMLInputElement
|
||||||
|
|
||||||
|
settings.relay_auth = target.checked ? RelayAuthMode.Aggressive : RelayAuthMode.Conservative
|
||||||
}
|
}
|
||||||
|
|
||||||
const onsubmit = preventDefault(async () => {
|
const onsubmit = preventDefault(async () => {
|
||||||
@@ -30,31 +33,46 @@
|
|||||||
<Icon icon={ShieldMinimalistic} />
|
<Icon icon={ShieldMinimalistic} />
|
||||||
Privacy Settings
|
Privacy Settings
|
||||||
</strong>
|
</strong>
|
||||||
<div class="grid grid-cols-2 gap-2">
|
<FieldInline>
|
||||||
<p>Authenticate with unknown relays?</p>
|
{#snippet label()}
|
||||||
<input
|
<p>Authenticate with unknown relays?</p>
|
||||||
type="checkbox"
|
{/snippet}
|
||||||
class="toggle toggle-primary"
|
{#snippet input()}
|
||||||
onchange={onAuthModeChange}
|
<input
|
||||||
checked={settings.auth_mode === RelayAuthMode.Aggressive} />
|
type="checkbox"
|
||||||
<p class="col-span-2 text-sm opacity-70">
|
class="toggle toggle-primary"
|
||||||
Controls whether {PLATFORM_NAME} will identify you to relays not in your lists.
|
onchange={onAuthModeChange}
|
||||||
</p>
|
checked={settings.relay_auth === RelayAuthMode.Aggressive} />
|
||||||
</div>
|
{/snippet}
|
||||||
<div class="grid grid-cols-2 gap-2">
|
{#snippet info()}
|
||||||
<p>Report errors?</p>
|
<p>Controls whether {PLATFORM_NAME} will identify you to relays not in your lists.</p>
|
||||||
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.report_errors} />
|
{/snippet}
|
||||||
<p class="col-span-2 text-sm opacity-70">
|
</FieldInline>
|
||||||
Allow {PLATFORM_NAME} to send error reports to help improve the app.
|
<FieldInline>
|
||||||
</p>
|
{#snippet label()}
|
||||||
</div>
|
<p>Report errors?</p>
|
||||||
<div class="grid grid-cols-2 gap-2">
|
{/snippet}
|
||||||
<p>Report usage?</p>
|
{#snippet input()}
|
||||||
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.report_usage} />
|
<input
|
||||||
<p class="col-span-2 text-sm opacity-70">
|
type="checkbox"
|
||||||
Allow {PLATFORM_NAME} to collect anonymous usage data.
|
class="toggle toggle-primary"
|
||||||
</p>
|
bind:checked={settings.report_errors} />
|
||||||
</div>
|
{/snippet}
|
||||||
|
{#snippet info()}
|
||||||
|
<p>Allow {PLATFORM_NAME} to send error reports to help improve the app.</p>
|
||||||
|
{/snippet}
|
||||||
|
</FieldInline>
|
||||||
|
<FieldInline>
|
||||||
|
{#snippet label()}
|
||||||
|
<p>Report usage?</p>
|
||||||
|
{/snippet}
|
||||||
|
{#snippet input()}
|
||||||
|
<input type="checkbox" class="toggle toggle-primary" bind:checked={settings.report_usage} />
|
||||||
|
{/snippet}
|
||||||
|
{#snippet info()}
|
||||||
|
<p>Allow {PLATFORM_NAME} to collect anonymous usage data.</p>
|
||||||
|
{/snippet}
|
||||||
|
</FieldInline>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
class="card2 bg-alt sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4">
|
class="card2 bg-alt sticky -bottom-3 shadow-md flex flex-row items-center justify-between gap-4">
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
import PasswordReset from "@app/components/PasswordReset.svelte"
|
import PasswordReset from "@app/components/PasswordReset.svelte"
|
||||||
import InfoKeys from "@app/components/InfoKeys.svelte"
|
import InfoKeys from "@app/components/InfoKeys.svelte"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
|
import {POMADE_NETWORK_ERROR_MESSAGE} from "@app/util/pomadeErrors"
|
||||||
import {clip, pushToast} from "@app/util/toast"
|
import {clip, pushToast} from "@app/util/toast"
|
||||||
|
|
||||||
const npub = nip19.npubEncode($pubkey!)
|
const npub = nip19.npubEncode($pubkey!)
|
||||||
@@ -48,13 +49,24 @@
|
|||||||
const {ok, peersByPrefix} = await Client.requestChallenge($session!.email)
|
const {ok, peersByPrefix} = await Client.requestChallenge($session!.email)
|
||||||
|
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
console.error("Pomade challenge request failed during password reset initiation")
|
||||||
|
|
||||||
pushToast({
|
pushToast({
|
||||||
theme: "error",
|
theme: "error",
|
||||||
message: "Failed to initiate password reset!",
|
message: POMADE_NETWORK_ERROR_MESSAGE,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pushModal(PasswordReset, {peersByPrefix})
|
pushModal(PasswordReset, {peersByPrefix})
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
|
||||||
|
pushToast({
|
||||||
|
theme: "error",
|
||||||
|
message: POMADE_NETWORK_ERROR_MESSAGE,
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
loading = false
|
loading = false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,9 @@
|
|||||||
import {derived as _derived} from "svelte/store"
|
import {derived as _derived} from "svelte/store"
|
||||||
import {dec, insertAt, removeAt, sleep} from "@welshman/lib"
|
import {dec, insertAt, removeAt, sleep} from "@welshman/lib"
|
||||||
import type {RelayProfile} from "@welshman/util"
|
import type {RelayProfile} from "@welshman/util"
|
||||||
|
import {ROOMS} from "@welshman/util"
|
||||||
import {throttled} from "@welshman/store"
|
import {throttled} from "@welshman/store"
|
||||||
import {relays, createSearch} from "@welshman/app"
|
import {pull, relays, createSearch} from "@welshman/app"
|
||||||
import {createScroller} from "@lib/html"
|
import {createScroller} from "@lib/html"
|
||||||
import {fly} from "@lib/transition"
|
import {fly} from "@lib/transition"
|
||||||
import DragHandle from "@assets/icons/drag-handle.svg?dataurl"
|
import DragHandle from "@assets/icons/drag-handle.svg?dataurl"
|
||||||
@@ -29,7 +30,9 @@
|
|||||||
userSpaceUrls,
|
userSpaceUrls,
|
||||||
loadUserGroupList,
|
loadUserGroupList,
|
||||||
PLATFORM_RELAYS,
|
PLATFORM_RELAYS,
|
||||||
|
DEFAULT_RELAYS,
|
||||||
groupListPubkeysByUrl,
|
groupListPubkeysByUrl,
|
||||||
|
bootstrapPubkeys,
|
||||||
parseInviteLink,
|
parseInviteLink,
|
||||||
} from "@app/core/state"
|
} from "@app/core/state"
|
||||||
import {setSpaceMembershipOrder} from "@app/core/commands"
|
import {setSpaceMembershipOrder} from "@app/core/commands"
|
||||||
@@ -197,6 +200,11 @@
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
pull({
|
||||||
|
filters: [{kinds: [ROOMS], authors: $bootstrapPubkeys}],
|
||||||
|
relays: DEFAULT_RELAYS,
|
||||||
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
scroller.stop()
|
scroller.stop()
|
||||||
}
|
}
|
||||||
@@ -205,41 +213,46 @@
|
|||||||
|
|
||||||
<Page>
|
<Page>
|
||||||
<PageBar>
|
<PageBar>
|
||||||
{#if showSearch}
|
<div class="flex items-center justify-between gap-4" in:fly>
|
||||||
<label class="input input-bordered input-sm flex flex-1 items-center gap-2" in:fly>
|
<div class="ellipsize flex items-center gap-2 whitespace-nowrap">
|
||||||
<Icon icon={Magnifier} />
|
<Icon icon={Widget} size={6} />
|
||||||
<input
|
<strong>Spaces</strong>
|
||||||
bind:this={searchInput}
|
|
||||||
bind:value={term}
|
|
||||||
class="min-w-0 grow"
|
|
||||||
type="text"
|
|
||||||
placeholder="Search for spaces..." />
|
|
||||||
<Button onclick={closeSearch} class="flex items-center">
|
|
||||||
<Icon icon={CloseCircle} />
|
|
||||||
</Button>
|
|
||||||
</label>
|
|
||||||
{:else}
|
|
||||||
<div class="flex items-center justify-between gap-4" in:fly>
|
|
||||||
<div class="ellipsize flex items-center gap-2 whitespace-nowrap">
|
|
||||||
<Icon icon={Widget} size={6} />
|
|
||||||
<strong>Spaces</strong>
|
|
||||||
</div>
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<button
|
|
||||||
class="btn btn-neutral btn-sm btn-square"
|
|
||||||
aria-label="Search"
|
|
||||||
onclick={openSearch}>
|
|
||||||
<Icon size={4} icon={Magnifier} />
|
|
||||||
</button>
|
|
||||||
{#if PLATFORM_RELAYS.length === 0}
|
|
||||||
<Button class="btn btn-primary btn-sm" onclick={addSpace}>
|
|
||||||
<Icon icon={AddCircle} />
|
|
||||||
Add Space
|
|
||||||
</Button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
<div class="flex items-center gap-2">
|
||||||
|
<button class="btn btn-neutral btn-sm btn-square" aria-label="Search" onclick={openSearch}>
|
||||||
|
<Icon size={4} icon={Magnifier} />
|
||||||
|
</button>
|
||||||
|
{#if showSearch}
|
||||||
|
<button class="fixed inset-0 z-feature" aria-label="Close search" onclick={closeSearch}
|
||||||
|
></button>
|
||||||
|
<div class="fixed top-sai right-sai left-content-full z-feature p-2">
|
||||||
|
<div
|
||||||
|
class="card2 card2-sm p-2! bg-alt flex flex-col shadow-md"
|
||||||
|
transition:fly={{y: -40, duration: 150}}>
|
||||||
|
<label class="input input-sm input-bordered flex w-full items-center gap-2">
|
||||||
|
<Icon size={4} icon={Magnifier} />
|
||||||
|
<input
|
||||||
|
bind:this={searchInput}
|
||||||
|
bind:value={term}
|
||||||
|
class="min-w-0 grow"
|
||||||
|
type="text"
|
||||||
|
placeholder="Search for spaces..."
|
||||||
|
onkeydown={e => e.key === "Escape" && closeSearch()} />
|
||||||
|
<Button onclick={closeSearch} class="flex items-center">
|
||||||
|
<Icon icon={CloseCircle} />
|
||||||
|
</Button>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
{#if PLATFORM_RELAYS.length === 0}
|
||||||
|
<Button class="btn btn-primary btn-sm" onclick={addSpace}>
|
||||||
|
<Icon icon={AddCircle} />
|
||||||
|
Add Space
|
||||||
|
</Button>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</PageBar>
|
</PageBar>
|
||||||
<PageContent class="flex flex-col gap-2 p-2 pt-4">
|
<PageContent class="flex flex-col gap-2 p-2 pt-4">
|
||||||
<div class="flex flex-col gap-2" bind:this={element}>
|
<div class="flex flex-col gap-2" bind:this={element}>
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import {onMount} from "svelte"
|
import {onDestroy, onMount} from "svelte"
|
||||||
import {readable} from "svelte/store"
|
import {readable} from "svelte/store"
|
||||||
import {page} from "$app/stores"
|
import {page} from "$app/stores"
|
||||||
import {goto} from "$app/navigation"
|
import {goto} from "$app/navigation"
|
||||||
import type {Readable} from "svelte/store"
|
import type {Readable} from "svelte/store"
|
||||||
|
import {debounce} from "throttle-debounce"
|
||||||
import {pubkey, publishThunk, waitForThunkError, joinRoom, leaveRoom} from "@welshman/app"
|
import {pubkey, publishThunk, waitForThunkError, joinRoom, leaveRoom} from "@welshman/app"
|
||||||
import {now, ifLet, int, formatTimestampAsDate, ago, MINUTE} from "@welshman/lib"
|
import {now, ifLet, int, formatTimestampAsDate, ago, MINUTE} from "@welshman/lib"
|
||||||
import type {MakeNonOptional} from "@welshman/lib"
|
import type {MakeNonOptional} from "@welshman/lib"
|
||||||
@@ -14,7 +15,7 @@
|
|||||||
import InfoCircle from "@assets/icons/info-circle.svg?dataurl"
|
import InfoCircle from "@assets/icons/info-circle.svg?dataurl"
|
||||||
import Login2 from "@assets/icons/login-3.svg?dataurl"
|
import Login2 from "@assets/icons/login-3.svg?dataurl"
|
||||||
import cx from "classnames"
|
import cx from "classnames"
|
||||||
import {slide, fade, fly} from "@lib/transition"
|
import {fade, fly} from "@lib/transition"
|
||||||
import Button from "@lib/components/Button.svelte"
|
import Button from "@lib/components/Button.svelte"
|
||||||
import Divider from "@lib/components/Divider.svelte"
|
import Divider from "@lib/components/Divider.svelte"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
@@ -48,7 +49,8 @@
|
|||||||
import {VideoCallLayout, videoCallLayout, videoTileCount} from "@app/call/video"
|
import {VideoCallLayout, videoCallLayout, videoTileCount} from "@app/call/video"
|
||||||
import {makeFeed} from "@app/core/requests"
|
import {makeFeed} from "@app/core/requests"
|
||||||
import {popKey} from "@lib/implicit"
|
import {popKey} from "@lib/implicit"
|
||||||
import {checked} from "@app/util/notifications"
|
import {checked, deferredRoomPath, setChecked} from "@app/util/notifications"
|
||||||
|
import {makeRoomPath} from "@app/util/routes"
|
||||||
import {pushModal} from "@app/util/modal"
|
import {pushModal} from "@app/util/modal"
|
||||||
import {pushToast} from "@app/util/toast"
|
import {pushToast} from "@app/util/toast"
|
||||||
|
|
||||||
@@ -76,6 +78,21 @@
|
|||||||
voiceConnectedHere && $videoCallLayout === VideoCallLayout.Video,
|
voiceConnectedHere && $videoCallLayout === VideoCallLayout.Video,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const roomPath = makeRoomPath(url, h)
|
||||||
|
|
||||||
|
const videoCallChatHidden = $derived(
|
||||||
|
voiceConnectedHere && $videoCallLayout === VideoCallLayout.Video,
|
||||||
|
)
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
deferredRoomPath.set(videoCallChatHidden ? roomPath : undefined)
|
||||||
|
if (voiceConnectedHere && !videoCallChatHidden) {
|
||||||
|
setChecked(roomPath)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
onDestroy(() => deferredRoomPath.set(undefined))
|
||||||
|
|
||||||
let prevVideoTileCount = $state(0)
|
let prevVideoTileCount = $state(0)
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
@@ -155,6 +172,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onSubmit = async ({content, tags}: EventContent) => {
|
const onSubmit = async ({content, tags}: EventContent) => {
|
||||||
|
if (!content && !share) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
tags.push(["h", h])
|
tags.push(["h", h])
|
||||||
|
|
||||||
@@ -244,6 +265,8 @@
|
|||||||
const onScroll = () => {
|
const onScroll = () => {
|
||||||
if (!isProgrammaticScroll) {
|
if (!isProgrammaticScroll) {
|
||||||
userHasScrolled = true
|
userHasScrolled = true
|
||||||
|
isUserScrolling = true
|
||||||
|
clearIsUserScrolling()
|
||||||
manageScrollPosition()
|
manageScrollPosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -265,6 +288,7 @@
|
|||||||
let leaving = $state(false)
|
let leaving = $state(false)
|
||||||
let userHasScrolled = $state(false)
|
let userHasScrolled = $state(false)
|
||||||
let isProgrammaticScroll = $state(false)
|
let isProgrammaticScroll = $state(false)
|
||||||
|
let isUserScrolling = $state(false)
|
||||||
let loadingBackward = $state(true)
|
let loadingBackward = $state(true)
|
||||||
let loadingForward = $state(true)
|
let loadingForward = $state(true)
|
||||||
let share = $state(popKey<TrustedEvent | undefined>("share"))
|
let share = $state(popKey<TrustedEvent | undefined>("share"))
|
||||||
@@ -278,6 +302,10 @@
|
|||||||
let compose: RoomCompose | undefined = $state()
|
let compose: RoomCompose | undefined = $state()
|
||||||
let eventToEdit: TrustedEvent | undefined = $state()
|
let eventToEdit: TrustedEvent | undefined = $state()
|
||||||
|
|
||||||
|
const clearIsUserScrolling = debounce(150, () => {
|
||||||
|
isUserScrolling = false
|
||||||
|
})
|
||||||
|
|
||||||
const elements = $derived.by(() => {
|
const elements = $derived.by(() => {
|
||||||
const elements = []
|
const elements = []
|
||||||
const seen = new Set()
|
const seen = new Set()
|
||||||
@@ -351,7 +379,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (elements.length > 0) {
|
if (elements.length > 0 && !isUserScrolling) {
|
||||||
requestAnimationFrame(manageScrollPosition)
|
requestAnimationFrame(manageScrollPosition)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -396,7 +424,8 @@
|
|||||||
onMount(() => {
|
onMount(() => {
|
||||||
start()
|
start()
|
||||||
|
|
||||||
return cleanup
|
// Wrap in a closure to avoid calling a stale cleanup function
|
||||||
|
return () => cleanup?.()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -439,9 +468,8 @@
|
|||||||
bind:element
|
bind:element
|
||||||
onscroll={onScroll}
|
onscroll={onScroll}
|
||||||
class={cx(
|
class={cx(
|
||||||
showMobileVideoPanel
|
"flex-col-reverse pb-0! pt-4",
|
||||||
? "hidden flex-col-reverse pt-4 md:flex md:flex-col-reverse"
|
showMobileVideoPanel ? "hidden md:flex md:flex-col-reverse" : "flex",
|
||||||
: "flex flex-col-reverse pt-4",
|
|
||||||
pageContentHiddenDesktopVideoOnly && "md:hidden",
|
pageContentHiddenDesktopVideoOnly && "md:hidden",
|
||||||
)}>
|
)}>
|
||||||
{#if $room.isPrivate && $membershipStatus !== MembershipStatus.Granted}
|
{#if $room.isPrivate && $membershipStatus !== MembershipStatus.Granted}
|
||||||
@@ -488,16 +516,14 @@
|
|||||||
{#if event.kind === ROOM_ADD_MEMBER}
|
{#if event.kind === ROOM_ADD_MEMBER}
|
||||||
<RoomItemAddMember {url} {event} />
|
<RoomItemAddMember {url} {event} />
|
||||||
{:else}
|
{:else}
|
||||||
<div in:slide class="cv">
|
<RoomItem
|
||||||
<RoomItem
|
{url}
|
||||||
{url}
|
{event}
|
||||||
{event}
|
{replyTo}
|
||||||
{replyTo}
|
{showPubkey}
|
||||||
{showPubkey}
|
{addSpaceBelow}
|
||||||
{addSpaceBelow}
|
canEdit={canEditEvent}
|
||||||
canEdit={canEditEvent}
|
onEdit={onEditEvent} />
|
||||||
onEdit={onEditEvent} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
import {goto} from "$app/navigation"
|
import {goto} from "$app/navigation"
|
||||||
import type {Readable} from "svelte/store"
|
import type {Readable} from "svelte/store"
|
||||||
import {readable} from "svelte/store"
|
import {readable} from "svelte/store"
|
||||||
|
import {debounce} from "throttle-debounce"
|
||||||
import {now, int, ifLet, formatTimestampAsDate, MINUTE, ago} from "@welshman/lib"
|
import {now, int, ifLet, formatTimestampAsDate, MINUTE, ago} from "@welshman/lib"
|
||||||
import type {TrustedEvent, EventContent} from "@welshman/util"
|
import type {TrustedEvent, EventContent} from "@welshman/util"
|
||||||
import {makeEvent, MESSAGE, RELAY_ADD_MEMBER} from "@welshman/util"
|
import {makeEvent, MESSAGE, RELAY_ADD_MEMBER} from "@welshman/util"
|
||||||
@@ -56,6 +57,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
const onSubmit = async ({content, tags}: EventContent) => {
|
const onSubmit = async ({content, tags}: EventContent) => {
|
||||||
|
if (!content && !share) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
let template: EventContent & {created_at?: number} = {content, tags}
|
let template: EventContent & {created_at?: number} = {content, tags}
|
||||||
|
|
||||||
@@ -139,6 +144,8 @@
|
|||||||
const onScroll = () => {
|
const onScroll = () => {
|
||||||
if (!isProgrammaticScroll) {
|
if (!isProgrammaticScroll) {
|
||||||
userHasScrolled = true
|
userHasScrolled = true
|
||||||
|
isUserScrolling = true
|
||||||
|
clearIsUserScrolling()
|
||||||
manageScrollPosition()
|
manageScrollPosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,6 +167,7 @@
|
|||||||
let loadingForward = $state(true)
|
let loadingForward = $state(true)
|
||||||
let userHasScrolled = $state(false)
|
let userHasScrolled = $state(false)
|
||||||
let isProgrammaticScroll = $state(false)
|
let isProgrammaticScroll = $state(false)
|
||||||
|
let isUserScrolling = $state(false)
|
||||||
let share = $state(popKey<TrustedEvent | undefined>("share"))
|
let share = $state(popKey<TrustedEvent | undefined>("share"))
|
||||||
let parent: TrustedEvent | undefined = $state()
|
let parent: TrustedEvent | undefined = $state()
|
||||||
let element: HTMLElement | undefined = $state()
|
let element: HTMLElement | undefined = $state()
|
||||||
@@ -171,6 +179,10 @@
|
|||||||
let compose: RoomCompose | undefined = $state()
|
let compose: RoomCompose | undefined = $state()
|
||||||
let eventToEdit: TrustedEvent | undefined = $state()
|
let eventToEdit: TrustedEvent | undefined = $state()
|
||||||
|
|
||||||
|
const clearIsUserScrolling = debounce(150, () => {
|
||||||
|
isUserScrolling = false
|
||||||
|
})
|
||||||
|
|
||||||
const elements = $derived.by(() => {
|
const elements = $derived.by(() => {
|
||||||
const elements = []
|
const elements = []
|
||||||
const seen = new Set()
|
const seen = new Set()
|
||||||
@@ -244,7 +256,7 @@
|
|||||||
})
|
})
|
||||||
|
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (elements.length > 0) {
|
if (elements.length > 0 && !isUserScrolling) {
|
||||||
requestAnimationFrame(manageScrollPosition)
|
requestAnimationFrame(manageScrollPosition)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -289,7 +301,8 @@
|
|||||||
onMount(() => {
|
onMount(() => {
|
||||||
start()
|
start()
|
||||||
|
|
||||||
return cleanup
|
// Wrap in a closure to avoid calling a stale cleanup function
|
||||||
|
return () => cleanup?.()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -303,7 +316,7 @@
|
|||||||
{/snippet}
|
{/snippet}
|
||||||
</SpaceBar>
|
</SpaceBar>
|
||||||
|
|
||||||
<PageContent bind:element onscroll={onScroll} class="flex flex-col-reverse pt-4 mb-14 md:mb-0">
|
<PageContent bind:element onscroll={onScroll} class="flex flex-col-reverse pt-4 pb-0!">
|
||||||
{#if loadingForward}
|
{#if loadingForward}
|
||||||
<p class="py-20 flex justify-center">
|
<p class="py-20 flex justify-center">
|
||||||
<Spinner loading={loadingForward}>Looking for messages...</Spinner>
|
<Spinner loading={loadingForward}>Looking for messages...</Spinner>
|
||||||
@@ -326,16 +339,14 @@
|
|||||||
{#if event.kind === RELAY_ADD_MEMBER}
|
{#if event.kind === RELAY_ADD_MEMBER}
|
||||||
<RoomItemAddMember {url} {event} />
|
<RoomItemAddMember {url} {event} />
|
||||||
{:else}
|
{:else}
|
||||||
<div>
|
<RoomItem
|
||||||
<RoomItem
|
{url}
|
||||||
{url}
|
{event}
|
||||||
{event}
|
{replyTo}
|
||||||
{replyTo}
|
{showPubkey}
|
||||||
{showPubkey}
|
canEdit={canEditEvent}
|
||||||
canEdit={canEditEvent}
|
onEdit={onEditEvent}
|
||||||
onEdit={onEditEvent}
|
{addSpaceBelow} />
|
||||||
{addSpaceBelow} />
|
|
||||||
</div>
|
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import Server from "@assets/icons/server.svg?dataurl"
|
import CloudCheck from "@assets/icons/cloud-check.svg?dataurl"
|
||||||
|
import CheckCircle from "@assets/icons/check-circle.svg?dataurl"
|
||||||
import ArrowRight from "@assets/icons/arrow-right.svg?dataurl"
|
import ArrowRight from "@assets/icons/arrow-right.svg?dataurl"
|
||||||
import Link from "@lib/components/Link.svelte"
|
import Link from "@lib/components/Link.svelte"
|
||||||
import Icon from "@lib/components/Icon.svelte"
|
import Icon from "@lib/components/Icon.svelte"
|
||||||
@@ -12,78 +13,91 @@
|
|||||||
<PageContent class="flex flex-col items-center gap-2 p-2 pt-4">
|
<PageContent class="flex flex-col items-center gap-2 p-2 pt-4">
|
||||||
<PageHeader>
|
<PageHeader>
|
||||||
{#snippet title()}
|
{#snippet title()}
|
||||||
<div>Create your own Space</div>
|
<div>Choose your Hosting Plan</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
{#snippet info()}
|
{#snippet info()}
|
||||||
<p>Get started with one of our trusted partners, or learn how to host your own space.</p>
|
<p>
|
||||||
|
Select how you want to deploy and manage your new Space. You can always migrate later.
|
||||||
|
</p>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
<div class="grid w-full max-w-lg grid-cols-1 gap-2 lg:max-w-4xl lg:grid-cols-2">
|
<div class="flex w-full max-w-lg flex-col gap-4 lg:max-w-4xl">
|
||||||
<div class="card2 bg-alt flex flex-col gap-4">
|
<div class="grid grid-cols-1 gap-2 lg:grid-cols-2">
|
||||||
<div class="flex flex-col gap-4">
|
<div class="card2 bg-alt flex flex-col gap-5">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex flex-col gap-3">
|
||||||
<div class="flex items-center gap-3">
|
<div class="bg-primary/20 flex h-10 w-10 items-center justify-center rounded-md">
|
||||||
<Icon icon={Server} />
|
<Icon icon={CloudCheck} class="text-primary" />
|
||||||
<h3 class="text-lg font-bold">Self-Host your Space</h3>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="badge badge-neutral">Recommended</div>
|
<div class="flex flex-col gap-1">
|
||||||
|
<h3 class="text-lg font-bold">Community</h3>
|
||||||
|
<div class="text-xs font-semibold tracking-wider opacity-60">SELF-HOSTED</div>
|
||||||
|
</div>
|
||||||
|
<p class="text-sm opacity-70">
|
||||||
|
For technical users who want full control. Deploy on your own infrastructure and
|
||||||
|
manage your own updates and scaling.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ul class="flex list-inside list-disc flex-col gap-1 text-sm opacity-70">
|
<ul class="flex flex-col gap-2 text-sm">
|
||||||
<li>Unlimited customization and control</li>
|
<li class="flex items-center gap-2">
|
||||||
<li>Free and open source software</li>
|
<Icon icon={CheckCircle} class="opacity-60" />
|
||||||
<li>Full-featured admin dashboards available</li>
|
Open source core
|
||||||
<li>Requires some technical skills</li>
|
</li>
|
||||||
|
<li class="flex items-center gap-2">
|
||||||
|
<Icon icon={CheckCircle} class="opacity-60" />
|
||||||
|
Community support
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center gap-2">
|
||||||
|
<Icon icon={CheckCircle} class="opacity-60" />
|
||||||
|
Bring your own infra
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<Link
|
||||||
|
external
|
||||||
|
class="btn btn-neutral mt-auto"
|
||||||
|
href="https://gitea.coracle.social/coracle/zooid">
|
||||||
|
Get started
|
||||||
|
<Icon icon={ArrowRight} />
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<Link external class="btn btn-primary" href="https://github.com/coracle-social/zooid">
|
<div class="card2 bg-alt border-primary flex flex-col gap-5 border">
|
||||||
Get Started
|
<div class="flex flex-col gap-3">
|
||||||
<Icon icon={ArrowRight} />
|
<div class="flex items-start justify-between">
|
||||||
</Link>
|
<img alt="Coracle Logo" src="/coracle.png" class="h-10 w-10" />
|
||||||
</div>
|
<div class="badge badge-primary">Recommended</div>
|
||||||
<div class="card2 bg-alt flex flex-col gap-4">
|
</div>
|
||||||
<div class="flex flex-col gap-4">
|
<div class="flex flex-col gap-1">
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div class="flex items-center gap-2">
|
|
||||||
<img alt="Coracle Logo" src="/coracle.png" class="h-7 w-7" />
|
|
||||||
<h3 class="text-lg font-bold">Coracle Hosting</h3>
|
<h3 class="text-lg font-bold">Coracle Hosting</h3>
|
||||||
|
<div class="text-xs font-semibold tracking-wider opacity-60">FULLY MANAGED</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="badge badge-neutral">Recommended</div>
|
<p class="text-sm opacity-70">
|
||||||
|
The premium experience. We handle the infrastructure, security updates, and scaling so
|
||||||
|
you can focus on your community.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
<ul class="flex list-inside list-disc flex-col gap-1 text-sm opacity-70">
|
<ul class="flex flex-col gap-2 text-sm">
|
||||||
<li>Simple setup, support included</li>
|
<li class="flex items-center gap-2">
|
||||||
<li>Free and open source software — no vendor lock-in</li>
|
<Icon icon={CheckCircle} class="text-primary" />
|
||||||
<li>Advanced access controls and relay policies</li>
|
One-click deployment
|
||||||
<li>Full-featured admin dashboard</li>
|
</li>
|
||||||
|
<li class="flex items-center gap-2">
|
||||||
|
<Icon icon={CheckCircle} class="text-primary" />
|
||||||
|
Automated backups & scaling
|
||||||
|
</li>
|
||||||
|
<li class="flex items-center gap-2">
|
||||||
|
<Icon icon={CheckCircle} class="text-primary" />
|
||||||
|
Priority support
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
<Link external class="btn btn-primary mt-auto" href="https://hosting.coracle.social">
|
||||||
|
Start for free
|
||||||
|
<Icon icon={ArrowRight} />
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<Link external class="btn btn-neutral" href="https://hosting.coracle.social">
|
|
||||||
Get Started
|
|
||||||
<Icon icon={ArrowRight} />
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="card2 bg-alt flex flex-col gap-4">
|
<div class="flex flex-col items-center justify-center gap-2 py-2 text-sm opacity-70">
|
||||||
<div class="flex flex-col gap-4">
|
<span>Want to host on other servers?</span>
|
||||||
<div class="self-start">
|
<Link external class="link center gap-1" href="https://relay.tools/signup">
|
||||||
<img
|
Other hosting options
|
||||||
alt="Relay Tools"
|
|
||||||
src="https://relay.tools/17.svg"
|
|
||||||
class="-my-20 -ml-2 hidden h-48 dark:block"
|
|
||||||
style="filter: contrast(50%)" />
|
|
||||||
<img
|
|
||||||
alt="Relay Tools"
|
|
||||||
src="https://relay.tools/19.svg"
|
|
||||||
class="-my-20 -ml-2 h-48 dark:hidden"
|
|
||||||
style="filter: contrast(50%)" />
|
|
||||||
</div>
|
|
||||||
<ul class="flex list-inside list-disc flex-col gap-1 text-sm opacity-70">
|
|
||||||
<li>Independently run</li>
|
|
||||||
<li>Customizable relay policies</li>
|
|
||||||
<li>Simple management dashboard</li>
|
|
||||||
<li>Support available</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
<Link external class="btn btn-neutral" href="https://relay.tools/signup">
|
|
||||||
Get Started
|
|
||||||
<Icon icon={ArrowRight} />
|
<Icon icon={ArrowRight} />
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user