diff --git a/.gitignore b/.gitignore
index 88f912a..f8ddb88 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,5 +4,5 @@ node_modules/
*.map
.DS_Store
.sourcemaps
-dist/
+/dist/
package-lock.json
\ No newline at end of file
diff --git a/ios/App/App.xcodeproj/project.pbxproj b/ios/App/App.xcodeproj/project.pbxproj
index 40be252..eb0f577 100644
--- a/ios/App/App.xcodeproj/project.pbxproj
+++ b/ios/App/App.xcodeproj/project.pbxproj
@@ -465,9 +465,9 @@
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- MARKETING_VERSION = 1.0.6;
+ MARKETING_VERSION = 1.0.8;
OTHER_SWIFT_FLAGS = "$(inherited) \"-D\" \"COCOAPODS\" \"-DDEBUG\"";
- PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forumtest;
+ PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forum;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_VERSION = 5.0;
@@ -488,8 +488,8 @@
INFOPLIST_FILE = App/Info.plist;
IPHONEOS_DEPLOYMENT_TARGET = 14.0;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
- MARKETING_VERSION = 1.0.6;
- PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forumtest;
+ MARKETING_VERSION = 1.0.8;
+ PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forum;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "";
SWIFT_VERSION = 5.0;
@@ -521,7 +521,7 @@
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
- PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forumtest.nse;
+ PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forum.nse;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
@@ -554,7 +554,7 @@
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
- PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forumtest.nse;
+ PRODUCT_BUNDLE_IDENTIFIER = russell.sam.forum.nse;
PRODUCT_NAME = "$(TARGET_NAME)";
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
diff --git a/ios/App/NotificationService/Info.plist b/ios/App/NotificationService/Info.plist
index ad04f44..08b3a97 100644
--- a/ios/App/NotificationService/Info.plist
+++ b/ios/App/NotificationService/Info.plist
@@ -2,10 +2,6 @@
- NSUserActivityTypes
-
- INSendMessageIntent
-
NSExtension
NSExtensionPointIdentifier
@@ -13,5 +9,9 @@
NSExtensionPrincipalClass
$(PRODUCT_MODULE_NAME).NotificationService
+ NSUserActivityTypes
+
+ INSendMessageIntent
+
diff --git a/native-input/dist/esm/definitions.d.ts b/native-input/dist/esm/definitions.d.ts
new file mode 100644
index 0000000..eb22d9b
--- /dev/null
+++ b/native-input/dist/esm/definitions.d.ts
@@ -0,0 +1,65 @@
+import type { PluginListenerHandle } from '@capacitor/core';
+
+/**
+ * Colors are CSS color strings resolved on the web side from the chat theme
+ * variables, so the native bar matches light/dark mode and the in-app palette.
+ */
+export interface NativeInputTheme {
+ /** Bar / text field background. From `--chat-input-bg`. */
+ inputBackground?: string;
+ /** Text + caret color. From `--chat-input-text`. */
+ text?: string;
+ /** Placeholder color. */
+ placeholder?: string;
+ /** Accent used for the "+" button. From `--accent`. */
+ accent?: string;
+ /** Send button background. From `--chat-sent-bg`. */
+ sendBackground?: string;
+ /** Send button glyph color. From `--chat-sent-text`. */
+ sendText?: string;
+ /** Whether the app is currently in dark mode (drives the glass/blur tint). */
+ dark?: boolean;
+}
+
+export interface PresentOptions {
+ /** Identifies the active chat so re-presents for a different chat reset text. */
+ chatId: string;
+ placeholder?: string;
+ /** Pre-fill text (e.g. a half-typed draft when re-mounting). */
+ text?: string;
+ /** When false, the field is read-only and the send button is disabled. */
+ canSend?: boolean;
+ theme?: NativeInputTheme;
+}
+
+export interface NativeInputPlugin {
+ /** Create + show the native input bar pinned above the keyboard. */
+ present(options: PresentOptions): Promise;
+ /** Remove the native input bar. */
+ dismiss(): Promise;
+ setText(options: { text: string }): Promise;
+ getText(): Promise<{ text: string }>;
+ focus(): Promise;
+ blur(): Promise;
+ setPlaceholder(options: { placeholder: string }): Promise;
+ setEnabled(options: { enabled: boolean }): Promise;
+ setTheme(options: { theme: NativeInputTheme }): Promise;
+ /** Hide/show without tearing down (e.g. when navigating away briefly). */
+ setVisible(options: { visible: boolean }): Promise;
+
+ /** Fires on every text edit. */
+ addListener(eventName: 'inputChange', listenerFunc: (data: { text: string }) => void): Promise;
+ /** Fires when the user taps send / hits the send key. */
+ addListener(eventName: 'send', listenerFunc: (data: { text: string }) => void): Promise;
+ /** Fires when the "+" attach button is tapped. */
+ addListener(eventName: 'attach', listenerFunc: () => void): Promise;
+ /** Fires on focus / blur of the native field. */
+ addListener(eventName: 'focusChange', listenerFunc: (data: { focused: boolean }) => void): Promise;
+ /**
+ * Fires when the space the bar (and keyboard) occupies changes, in CSS px,
+ * so the web layout can pad its scroll area / position the reply strip.
+ */
+ addListener(eventName: 'heightChange', listenerFunc: (data: { bottomInset: number }) => void): Promise;
+
+ removeAllListeners(): Promise;
+}
diff --git a/native-input/dist/esm/definitions.js b/native-input/dist/esm/definitions.js
new file mode 100644
index 0000000..cb0ff5c
--- /dev/null
+++ b/native-input/dist/esm/definitions.js
@@ -0,0 +1 @@
+export {};
diff --git a/native-input/dist/esm/index.d.ts b/native-input/dist/esm/index.d.ts
new file mode 100644
index 0000000..c8cfd75
--- /dev/null
+++ b/native-input/dist/esm/index.d.ts
@@ -0,0 +1,4 @@
+import type { NativeInputPlugin } from './definitions';
+declare const NativeInput: NativeInputPlugin;
+export * from './definitions';
+export { NativeInput };
diff --git a/native-input/dist/esm/index.js b/native-input/dist/esm/index.js
new file mode 100644
index 0000000..77589a5
--- /dev/null
+++ b/native-input/dist/esm/index.js
@@ -0,0 +1,8 @@
+import { registerPlugin } from '@capacitor/core';
+
+const NativeInput = registerPlugin('NativeInput', {
+ web: () => import('./web').then(m => new m.NativeInputWeb()),
+});
+
+export * from './definitions';
+export { NativeInput };
diff --git a/native-input/dist/esm/web.d.ts b/native-input/dist/esm/web.d.ts
new file mode 100644
index 0000000..3f5afd5
--- /dev/null
+++ b/native-input/dist/esm/web.d.ts
@@ -0,0 +1,15 @@
+import { WebPlugin } from '@capacitor/core';
+import type { NativeInputPlugin } from './definitions';
+export declare class NativeInputWeb extends WebPlugin implements NativeInputPlugin {
+ present(): Promise;
+ dismiss(): Promise;
+ setText(): Promise;
+ getText(): Promise<{ text: string }>;
+ focus(): Promise;
+ blur(): Promise;
+ setPlaceholder(): Promise;
+ setEnabled(): Promise;
+ setTheme(): Promise;
+ setVisible(): Promise;
+ addListener(eventName: any, listenerFunc: any): Promise;
+}
diff --git a/native-input/dist/esm/web.js b/native-input/dist/esm/web.js
new file mode 100644
index 0000000..245235d
--- /dev/null
+++ b/native-input/dist/esm/web.js
@@ -0,0 +1,17 @@
+import { WebPlugin } from '@capacitor/core';
+
+// No-op web implementation so the plugin import resolves in the browser build.
+// The HTML composer (textarea) is used on web; these methods are never the
+// active path there, but must exist to avoid runtime crashes.
+export class NativeInputWeb extends WebPlugin {
+ async present() { /* no-op on web */ }
+ async dismiss() { /* no-op on web */ }
+ async setText() { /* no-op on web */ }
+ async getText() { return { text: '' }; }
+ async focus() { /* no-op on web */ }
+ async blur() { /* no-op on web */ }
+ async setPlaceholder() { /* no-op on web */ }
+ async setEnabled() { /* no-op on web */ }
+ async setTheme() { /* no-op on web */ }
+ async setVisible() { /* no-op on web */ }
+}
diff --git a/native-input/dist/plugin.cjs.js b/native-input/dist/plugin.cjs.js
new file mode 100644
index 0000000..ddef378
--- /dev/null
+++ b/native-input/dist/plugin.cjs.js
@@ -0,0 +1,25 @@
+'use strict';
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+const core = require('@capacitor/core');
+
+class NativeInputWeb extends core.WebPlugin {
+ async present() {}
+ async dismiss() {}
+ async setText() {}
+ async getText() { return { text: '' }; }
+ async focus() {}
+ async blur() {}
+ async setPlaceholder() {}
+ async setEnabled() {}
+ async setTheme() {}
+ async setVisible() {}
+}
+
+const NativeInput = core.registerPlugin('NativeInput', {
+ web: () => Promise.resolve(new NativeInputWeb()),
+});
+
+exports.NativeInput = NativeInput;
+exports.NativeInputWeb = NativeInputWeb;
diff --git a/native-sidebar/dist/esm/definitions.d.ts b/native-sidebar/dist/esm/definitions.d.ts
new file mode 100644
index 0000000..9ec9261
--- /dev/null
+++ b/native-sidebar/dist/esm/definitions.d.ts
@@ -0,0 +1,65 @@
+import type { PluginListenerHandle } from '@capacitor/core';
+
+/** A network row rendered in the sidebar. Shapes match `global.networks`. */
+export interface SidebarNetwork {
+ id: number;
+ name: string;
+ abbreviation: string;
+ logo?: string | null;
+ displayOrder?: number;
+ data?: {
+ announcements?: { text: string }[];
+ members?: { id: number }[];
+ };
+}
+
+export interface SidebarProfile {
+ id: number;
+ first_name?: string;
+ last_name?: string;
+ email?: string;
+ image_path?: string | null;
+}
+
+export interface SidebarData {
+ /** Origin that serves the UI (quill.js, Sidebar.js, host page). */
+ uiOrigin: string;
+ /** Origin that serves images (logos, avatars). */
+ serverOrigin: string;
+ profile: SidebarProfile;
+ networks: SidebarNetwork[];
+ currentNetworkId?: number | null;
+ /** Current theme key: light | parchment | dark | red. */
+ theme?: string;
+ /** Whether the current user may create networks. */
+ canCreateNetwork?: boolean;
+}
+
+/** An action forwarded from the sidebar's content web view to the main app. */
+export interface SidebarAction {
+ type: 'selectNetwork' | 'openProfile' | 'createNetwork' | 'setTheme' | 'logout';
+ abbreviation?: string;
+ value?: string;
+}
+
+export interface NativeSidebarPlugin {
+ /** Create (pre-warm) the drawer if needed and push the latest data into it. */
+ setData(data: SidebarData): Promise;
+ /** Update just the theme (key) without re-sending all data. */
+ setTheme(options: { theme: string }): Promise;
+ /** Animate the drawer open. */
+ open(): Promise;
+ /** Animate the drawer closed. */
+ close(): Promise;
+ /** Enable/disable the edge-drag gesture (disabled on deep routes). */
+ setEnabled(options: { enabled: boolean }): Promise;
+ /** Tear down the drawer entirely. */
+ dismiss(): Promise;
+
+ /** Fires when the user taps something in the sidebar content. */
+ addListener(eventName: 'action', listenerFunc: (data: SidebarAction) => void): Promise;
+ /** Fires when the drawer finishes opening/closing. */
+ addListener(eventName: 'stateChange', listenerFunc: (data: { open: boolean }) => void): Promise;
+
+ removeAllListeners(): Promise;
+}
diff --git a/native-sidebar/dist/esm/definitions.js b/native-sidebar/dist/esm/definitions.js
new file mode 100644
index 0000000..cb0ff5c
--- /dev/null
+++ b/native-sidebar/dist/esm/definitions.js
@@ -0,0 +1 @@
+export {};
diff --git a/native-sidebar/dist/esm/index.d.ts b/native-sidebar/dist/esm/index.d.ts
new file mode 100644
index 0000000..c29e992
--- /dev/null
+++ b/native-sidebar/dist/esm/index.d.ts
@@ -0,0 +1,4 @@
+import type { NativeSidebarPlugin } from './definitions';
+declare const NativeSidebar: NativeSidebarPlugin;
+export * from './definitions';
+export { NativeSidebar };
diff --git a/native-sidebar/dist/esm/index.js b/native-sidebar/dist/esm/index.js
new file mode 100644
index 0000000..ee341ae
--- /dev/null
+++ b/native-sidebar/dist/esm/index.js
@@ -0,0 +1,8 @@
+import { registerPlugin } from '@capacitor/core';
+
+const NativeSidebar = registerPlugin('NativeSidebar', {
+ web: () => import('./web').then(m => new m.NativeSidebarWeb()),
+});
+
+export * from './definitions';
+export { NativeSidebar };
diff --git a/native-sidebar/dist/esm/web.d.ts b/native-sidebar/dist/esm/web.d.ts
new file mode 100644
index 0000000..048e0d7
--- /dev/null
+++ b/native-sidebar/dist/esm/web.d.ts
@@ -0,0 +1,11 @@
+import { WebPlugin } from '@capacitor/core';
+import type { NativeSidebarPlugin } from './definitions';
+export declare class NativeSidebarWeb extends WebPlugin implements NativeSidebarPlugin {
+ setData(): Promise;
+ setTheme(): Promise;
+ open(): Promise;
+ close(): Promise;
+ setEnabled(): Promise;
+ dismiss(): Promise;
+ addListener(eventName: any, listenerFunc: any): Promise;
+}
diff --git a/native-sidebar/dist/esm/web.js b/native-sidebar/dist/esm/web.js
new file mode 100644
index 0000000..37c7d5d
--- /dev/null
+++ b/native-sidebar/dist/esm/web.js
@@ -0,0 +1,13 @@
+import { WebPlugin } from '@capacitor/core';
+
+// No-op web implementation so the plugin import resolves in the browser build.
+// The drawer is iOS-native only; on web these methods are never the active path
+// but must exist to avoid runtime crashes.
+export class NativeSidebarWeb extends WebPlugin {
+ async setData() { /* no-op on web */ }
+ async setTheme() { /* no-op on web */ }
+ async open() { /* no-op on web */ }
+ async close() { /* no-op on web */ }
+ async setEnabled() { /* no-op on web */ }
+ async dismiss() { /* no-op on web */ }
+}
diff --git a/native-sidebar/dist/plugin.cjs.js b/native-sidebar/dist/plugin.cjs.js
new file mode 100644
index 0000000..1afe9f8
--- /dev/null
+++ b/native-sidebar/dist/plugin.cjs.js
@@ -0,0 +1,21 @@
+'use strict';
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+const core = require('@capacitor/core');
+
+class NativeSidebarWeb extends core.WebPlugin {
+ async setData() {}
+ async setTheme() {}
+ async open() {}
+ async close() {}
+ async setEnabled() {}
+ async dismiss() {}
+}
+
+const NativeSidebar = core.registerPlugin('NativeSidebar', {
+ web: () => Promise.resolve(new NativeSidebarWeb()),
+});
+
+exports.NativeSidebar = NativeSidebar;
+exports.NativeSidebarWeb = NativeSidebarWeb;
diff --git a/readme.md b/readme.md
index 03b5164..a37d733 100644
--- a/readme.md
+++ b/readme.md
@@ -55,7 +55,6 @@ Remove the "server" object from capacitor.config.
npx cap config - this will list the full configuration currently being used
### Architecture
-
In Development, API routes are routed using the vite.config.js.
### Notes