diff --git a/.changeset/pre.json b/.changeset/pre.json
index 925bc9e3..9b57e8d9 100644
--- a/.changeset/pre.json
+++ b/.changeset/pre.json
@@ -2,7 +2,6 @@
"mode": "pre",
"tag": "beta",
"initialVersions": {
- "@pulse-editor/capacitor-plugin": "0.0.1",
"@pulse-editor/mobile": "0.0.1",
"@pulse-editor/react-api": "0.1.1-alpha.54",
"@pulse-editor/shared-utils": "0.1.1-alpha.54",
diff --git a/.github/workflows/build-desktop.yml b/.github/workflows/build-desktop.yml
index caa7ec77..3b842587 100644
--- a/.github/workflows/build-desktop.yml
+++ b/.github/workflows/build-desktop.yml
@@ -32,13 +32,7 @@ jobs:
- name: Build shared-utils
run: npm run shared-utils-build
-
- - name: Install capacitor-plugin dependencies
- run: npm install --workspace=capacitor-plugin
-
- - name: Build capacitor-plugin
- run: npm run capacitor-plugin-build
-
+
- name: Install web dependencies
run: npm install --workspace=web
diff --git a/.github/workflows/build-mobile.yml b/.github/workflows/build-mobile.yml
index 46cd344d..43dc14a0 100644
--- a/.github/workflows/build-mobile.yml
+++ b/.github/workflows/build-mobile.yml
@@ -42,7 +42,7 @@ jobs:
- name: Get Keystore
run: |
mkdir ~/.keystore
- echo ${{ secrets.ANDROID_KEYSTORE }} | base64 --decode > ~/.keystore/keystore.jks
+ echo ${{ secrets.ANDROID_KEYSTORE }} | base64 --decode > ~/.keystore/pulse-editor.keystore
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -55,12 +55,6 @@ jobs:
- name: Build shared-utils
run: npm run shared-utils-build
- - name: Install capacitor-plugin dependencies
- run: npm install --workspace=capacitor-plugin
-
- - name: Build capacitor-plugin
- run: npm run capacitor-plugin-build
-
- name: Install web dependencies
run: npm install --workspace=web
@@ -78,5 +72,5 @@ jobs:
working-directory: mobile
- name: Build Capacitor App
- run: npx cap build android --keystorepath ~/.keystore/keystore.jks --keystorepass ${{ secrets.ANDROID_KEYSTORE_PASS }} --androidreleasetype APK
+ run: npx cap build android --keystorepath ~/.keystore/pulse-editor.keystore --keystorepass ${{ secrets.ANDROID_KEYSTORE_PASS }} --androidreleasetype APK
working-directory: mobile
diff --git a/.github/workflows/build-web.yml b/.github/workflows/build-web.yml
index ce6f5486..d53700ec 100644
--- a/.github/workflows/build-web.yml
+++ b/.github/workflows/build-web.yml
@@ -28,12 +28,6 @@ jobs:
- name: Build shared-utils
run: npm run shared-utils-build
-
- - name: Install capacitor-plugin dependencies
- run: npm install --workspace=capacitor-plugin
-
- - name: Build capacitor-plugin
- run: npm run capacitor-plugin-build
- name: Install web dependencies
run: npm install --workspace=web
diff --git a/.github/workflows/release-desktop.yml b/.github/workflows/release-desktop.yml
index dd78a626..a5800311 100644
--- a/.github/workflows/release-desktop.yml
+++ b/.github/workflows/release-desktop.yml
@@ -30,12 +30,6 @@ jobs:
- name: Build shared-utils
run: npm run shared-utils-build
- - name: Install capacitor-plugin dependencies
- run: npm install --workspace=capacitor-plugin
-
- - name: Build capacitor-plugin
- run: npm run capacitor-plugin-build
-
- name: Install web dependencies
run: npm install --workspace=web
diff --git a/.github/workflows/release-mobile.yml b/.github/workflows/release-mobile.yml
index 00823cf4..7590004e 100644
--- a/.github/workflows/release-mobile.yml
+++ b/.github/workflows/release-mobile.yml
@@ -39,7 +39,7 @@ jobs:
- name: Get Keystore
run: |
mkdir ~/.keystore
- echo ${{ secrets.ANDROID_KEYSTORE }} | base64 --decode > ~/.keystore/keystore.jks
+ echo ${{ secrets.ANDROID_KEYSTORE }} | base64 --decode > ~/.keystore/pulse-editor.keystore
- name: Setup Node.js
uses: actions/setup-node@v4
@@ -52,12 +52,6 @@ jobs:
- name: Build shared-utils
run: npm run shared-utils-build
- - name: Install capacitor-plugin dependencies
- run: npm install --workspace=capacitor-plugin
-
- - name: Build capacitor-plugin
- run: npm run capacitor-plugin-build
-
- name: Install web dependencies
run: npm install --workspace=web
@@ -75,7 +69,7 @@ jobs:
working-directory: mobile
- name: Build Capacitor App
- run: npx cap build android --keystorepath ~/.keystore/keystore.jks --keystorepass ${{ secrets.ANDROID_KEYSTORE_PASS }} --androidreleasetype APK
+ run: npx cap build android --keystorepath ~/.keystore/pulse-editor.keystore --keystorepass ${{ secrets.ANDROID_KEYSTORE_PASS }} --androidreleasetype APK
working-directory: mobile
- name: Move APK
diff --git a/capacitor-plugin/.eslintignore b/capacitor-plugin/.eslintignore
deleted file mode 100644
index 23f89447..00000000
--- a/capacitor-plugin/.eslintignore
+++ /dev/null
@@ -1,3 +0,0 @@
-build
-dist
-example-app
diff --git a/capacitor-plugin/.gitignore b/capacitor-plugin/.gitignore
deleted file mode 100644
index df9f0c20..00000000
--- a/capacitor-plugin/.gitignore
+++ /dev/null
@@ -1,70 +0,0 @@
-# node files
-dist
-node_modules
-
-# iOS files
-Pods
-Podfile.lock
-Package.resolved
-Build
-xcuserdata
-/.build
-/Packages
-xcuserdata/
-DerivedData/
-.swiftpm/configuration/registries.json
-.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
-.netrc
-
-
-# macOS files
-.DS_Store
-
-
-
-# Based on Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore
-
-# Built application files
-*.apk
-*.ap_
-
-# Files for the ART/Dalvik VM
-*.dex
-
-# Java class files
-*.class
-
-# Generated files
-bin
-gen
-out
-
-# Gradle files
-.gradle
-build
-
-# Local configuration file (sdk path, etc)
-local.properties
-
-# Proguard folder generated by Eclipse
-proguard
-
-# Log Files
-*.log
-
-# Android Studio Navigation editor temp files
-.navigation
-
-# Android Studio captures folder
-captures
-
-# IntelliJ
-*.iml
-.idea
-
-# Keystore files
-# Uncomment the following line if you do not want to check your keystore files in.
-#*.jks
-
-# External native build folder generated in Android Studio 2.2 and later
-.externalNativeBuild
diff --git a/capacitor-plugin/.prettierignore b/capacitor-plugin/.prettierignore
deleted file mode 100644
index 5ab6e14d..00000000
--- a/capacitor-plugin/.prettierignore
+++ /dev/null
@@ -1 +0,0 @@
-example-app
diff --git a/capacitor-plugin/CONTRIBUTING.md b/capacitor-plugin/CONTRIBUTING.md
deleted file mode 100644
index 3f875180..00000000
--- a/capacitor-plugin/CONTRIBUTING.md
+++ /dev/null
@@ -1,52 +0,0 @@
-# Contributing
-
-This guide provides instructions for contributing to this Capacitor plugin.
-
-## Developing
-
-### Local Setup
-
-1. Fork and clone the repo.
-1. Install the dependencies.
-
- ```shell
- npm install
- ```
-
-1. Install SwiftLint if you're on macOS.
-
- ```shell
- brew install swiftlint
- ```
-
-### Scripts
-
-#### `npm run build`
-
-Build the plugin web assets and generate plugin API documentation using [`@capacitor/docgen`](https://github.com/ionic-team/capacitor-docgen).
-
-It will compile the TypeScript code from `src/` into ESM JavaScript in `dist/esm/`. These files are used in apps with bundlers when your plugin is imported.
-
-Then, Rollup will bundle the code into a single file at `dist/plugin.js`. This file is used in apps without bundlers by including it as a script in `index.html`.
-
-#### `npm run verify`
-
-Build and validate the web and native projects.
-
-This is useful to run in CI to verify that the plugin builds for all platforms.
-
-#### `npm run lint` / `npm run fmt`
-
-Check formatting and code quality, autoformat/autofix if possible.
-
-This template is integrated with ESLint, Prettier, and SwiftLint. Using these tools is completely optional, but the [Capacitor Community](https://github.com/capacitor-community/) strives to have consistent code style and structure for easier cooperation.
-
-## Publishing
-
-There is a `prepublishOnly` hook in `package.json` which prepares the plugin before publishing, so all you need to do is run:
-
-```shell
-npm publish
-```
-
-> **Note**: The [`files`](https://docs.npmjs.com/cli/v7/configuring-npm/package-json#files) array in `package.json` specifies which files get published. If you rename files/directories or add files elsewhere, you may need to update it.
diff --git a/capacitor-plugin/Package.swift b/capacitor-plugin/Package.swift
deleted file mode 100644
index cabd9ee1..00000000
--- a/capacitor-plugin/Package.swift
+++ /dev/null
@@ -1,28 +0,0 @@
-// swift-tools-version: 5.9
-import PackageDescription
-
-let package = Package(
- name: "PulseEditorCapacitorPlugin",
- platforms: [.iOS(.v14)],
- products: [
- .library(
- name: "PulseEditorCapacitorPlugin",
- targets: ["PulseEditorCapacitorPlugin"])
- ],
- dependencies: [
- .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "7.0.0")
- ],
- targets: [
- .target(
- name: "PulseEditorCapacitorPlugin",
- dependencies: [
- .product(name: "Capacitor", package: "capacitor-swift-pm"),
- .product(name: "Cordova", package: "capacitor-swift-pm")
- ],
- path: "ios/Sources/PulseEditorCapacitorPlugin"),
- .testTarget(
- name: "PulseEditorCapacitorPluginTests",
- dependencies: ["PulseEditorCapacitorPlugin"],
- path: "ios/Tests/PulseEditorCapacitorPluginTests")
- ]
-)
\ No newline at end of file
diff --git a/capacitor-plugin/PulseEditorCapacitorPlugin.podspec b/capacitor-plugin/PulseEditorCapacitorPlugin.podspec
deleted file mode 100644
index db20bf67..00000000
--- a/capacitor-plugin/PulseEditorCapacitorPlugin.podspec
+++ /dev/null
@@ -1,17 +0,0 @@
-require 'json'
-
-package = JSON.parse(File.read(File.join(__dir__, 'package.json')))
-
-Pod::Spec.new do |s|
- s.name = 'PulseEditorCapacitorPlugin'
- s.version = package['version']
- s.summary = package['description']
- s.license = package['license']
- s.homepage = package['repository']['url']
- s.author = package['author']
- s.source = { :git => package['repository']['url'], :tag => s.version.to_s }
- s.source_files = 'ios/Sources/**/*.{swift,h,m,c,cc,mm,cpp}'
- s.ios.deployment_target = '14.0'
- s.dependency 'Capacitor'
- s.swift_version = '5.1'
-end
diff --git a/capacitor-plugin/README.md b/capacitor-plugin/README.md
deleted file mode 100644
index d4c17e4d..00000000
--- a/capacitor-plugin/README.md
+++ /dev/null
@@ -1,59 +0,0 @@
-# @pulse-editor/capacitor-plugin
-
-Capacitor plugins for Pulse Editor
-
-## Install
-
-```bash
-npm install @pulse-editor/capacitor-plugin
-npx cap sync
-```
-
-## API
-
-
-
-* [`echo(...)`](#echo)
-* [`startManageStorageIntent()`](#startmanagestorageintent)
-* [`isManageStoragePermissionGranted()`](#ismanagestoragepermissiongranted)
-
-
-
-
-
-
-### echo(...)
-
-```typescript
-echo(options: { value: string; }) => Promise<{ value: string; }>
-```
-
-| Param | Type |
-| ------------- | ------------------------------- |
-| **`options`** | { value: string; } |
-
-**Returns:** Promise<{ value: string; }>
-
---------------------
-
-
-### startManageStorageIntent()
-
-```typescript
-startManageStorageIntent() => Promise
-```
-
---------------------
-
-
-### isManageStoragePermissionGranted()
-
-```typescript
-isManageStoragePermissionGranted() => Promise<{ isGranted: boolean; }>
-```
-
-**Returns:** Promise<{ isGranted: boolean; }>
-
---------------------
-
-
diff --git a/capacitor-plugin/android/.gitignore b/capacitor-plugin/android/.gitignore
deleted file mode 100644
index 796b96d1..00000000
--- a/capacitor-plugin/android/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
diff --git a/capacitor-plugin/android/build.gradle b/capacitor-plugin/android/build.gradle
deleted file mode 100644
index 8bfe171f..00000000
--- a/capacitor-plugin/android/build.gradle
+++ /dev/null
@@ -1,58 +0,0 @@
-ext {
- junitVersion = project.hasProperty('junitVersion') ? rootProject.ext.junitVersion : '4.13.2'
- androidxAppCompatVersion = project.hasProperty('androidxAppCompatVersion') ? rootProject.ext.androidxAppCompatVersion : '1.7.0'
- androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.2.1'
- androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.6.1'
-}
-
-buildscript {
- repositories {
- google()
- mavenCentral()
- }
- dependencies {
- classpath 'com.android.tools.build:gradle:8.7.2'
- }
-}
-
-apply plugin: 'com.android.library'
-
-android {
- namespace "com.pulse.capacitor.plugin"
- compileSdk project.hasProperty('compileSdkVersion') ? rootProject.ext.compileSdkVersion : 35
- defaultConfig {
- minSdkVersion project.hasProperty('minSdkVersion') ? rootProject.ext.minSdkVersion : 23
- targetSdkVersion project.hasProperty('targetSdkVersion') ? rootProject.ext.targetSdkVersion : 35
- versionCode 1
- versionName "1.0"
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- }
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
- lintOptions {
- abortOnError false
- }
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_21
- targetCompatibility JavaVersion.VERSION_21
- }
-}
-
-repositories {
- google()
- mavenCentral()
-}
-
-
-dependencies {
- implementation fileTree(dir: 'libs', include: ['*.jar'])
- implementation project(':capacitor-android')
- implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion"
- testImplementation "junit:junit:$junitVersion"
- androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion"
- androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion"
-}
diff --git a/capacitor-plugin/android/gradle.properties b/capacitor-plugin/android/gradle.properties
deleted file mode 100644
index 2e87c52f..00000000
--- a/capacitor-plugin/android/gradle.properties
+++ /dev/null
@@ -1,22 +0,0 @@
-# Project-wide Gradle settings.
-
-# IDE (e.g. Android Studio) users:
-# Gradle settings configured through the IDE *will override*
-# any settings specified in this file.
-
-# For more details on how to configure your build environment visit
-# http://www.gradle.org/docs/current/userguide/build_environment.html
-
-# Specifies the JVM arguments used for the daemon process.
-# The setting is particularly useful for tweaking memory settings.
-org.gradle.jvmargs=-Xmx1536m
-
-# When configured, Gradle will run in incubating parallel mode.
-# This option should only be used with decoupled projects. More details, visit
-# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
-# org.gradle.parallel=true
-
-# AndroidX package structure to make it clearer which packages are bundled with the
-# Android operating system, and which are packaged with your app's APK
-# https://developer.android.com/topic/libraries/support-library/androidx-rn
-android.useAndroidX=true
diff --git a/capacitor-plugin/android/gradle/wrapper/gradle-wrapper.jar b/capacitor-plugin/android/gradle/wrapper/gradle-wrapper.jar
deleted file mode 100644
index a4b76b95..00000000
Binary files a/capacitor-plugin/android/gradle/wrapper/gradle-wrapper.jar and /dev/null differ
diff --git a/capacitor-plugin/android/gradle/wrapper/gradle-wrapper.properties b/capacitor-plugin/android/gradle/wrapper/gradle-wrapper.properties
deleted file mode 100644
index c1d5e018..00000000
--- a/capacitor-plugin/android/gradle/wrapper/gradle-wrapper.properties
+++ /dev/null
@@ -1,7 +0,0 @@
-distributionBase=GRADLE_USER_HOME
-distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-all.zip
-networkTimeout=10000
-validateDistributionUrl=true
-zipStoreBase=GRADLE_USER_HOME
-zipStorePath=wrapper/dists
diff --git a/capacitor-plugin/android/gradlew b/capacitor-plugin/android/gradlew
deleted file mode 100644
index f5feea6d..00000000
--- a/capacitor-plugin/android/gradlew
+++ /dev/null
@@ -1,252 +0,0 @@
-#!/bin/sh
-
-#
-# Copyright © 2015-2021 the original authors.
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# https://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-#
-# SPDX-License-Identifier: Apache-2.0
-#
-
-##############################################################################
-#
-# Gradle start up script for POSIX generated by Gradle.
-#
-# Important for running:
-#
-# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
-# noncompliant, but you have some other compliant shell such as ksh or
-# bash, then to run this script, type that shell name before the whole
-# command line, like:
-#
-# ksh Gradle
-#
-# Busybox and similar reduced shells will NOT work, because this script
-# requires all of these POSIX shell features:
-# * functions;
-# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
-# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
-# * compound commands having a testable exit status, especially «case»;
-# * various built-in commands including «command», «set», and «ulimit».
-#
-# Important for patching:
-#
-# (2) This script targets any POSIX shell, so it avoids extensions provided
-# by Bash, Ksh, etc; in particular arrays are avoided.
-#
-# The "traditional" practice of packing multiple parameters into a
-# space-separated string is a well documented source of bugs and security
-# problems, so this is (mostly) avoided, by progressively accumulating
-# options in "$@", and eventually passing that to Java.
-#
-# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
-# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
-# see the in-line comments for details.
-#
-# There are tweaks for specific operating systems such as AIX, CygWin,
-# Darwin, MinGW, and NonStop.
-#
-# (3) This script is generated from the Groovy template
-# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
-# within the Gradle project.
-#
-# You can find Gradle at https://github.com/gradle/gradle/.
-#
-##############################################################################
-
-# Attempt to set APP_HOME
-
-# Resolve links: $0 may be a link
-app_path=$0
-
-# Need this for daisy-chained symlinks.
-while
- APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
- [ -h "$app_path" ]
-do
- ls=$( ls -ld "$app_path" )
- link=${ls#*' -> '}
- case $link in #(
- /*) app_path=$link ;; #(
- *) app_path=$APP_HOME$link ;;
- esac
-done
-
-# This is normally unused
-# shellcheck disable=SC2034
-APP_BASE_NAME=${0##*/}
-# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
-APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s
-' "$PWD" ) || exit
-
-# Use the maximum available, or set MAX_FD != -1 to use that value.
-MAX_FD=maximum
-
-warn () {
- echo "$*"
-} >&2
-
-die () {
- echo
- echo "$*"
- echo
- exit 1
-} >&2
-
-# OS specific support (must be 'true' or 'false').
-cygwin=false
-msys=false
-darwin=false
-nonstop=false
-case "$( uname )" in #(
- CYGWIN* ) cygwin=true ;; #(
- Darwin* ) darwin=true ;; #(
- MSYS* | MINGW* ) msys=true ;; #(
- NONSTOP* ) nonstop=true ;;
-esac
-
-CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
-
-
-# Determine the Java command to use to start the JVM.
-if [ -n "$JAVA_HOME" ] ; then
- if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
- # IBM's JDK on AIX uses strange locations for the executables
- JAVACMD=$JAVA_HOME/jre/sh/java
- else
- JAVACMD=$JAVA_HOME/bin/java
- fi
- if [ ! -x "$JAVACMD" ] ; then
- die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-else
- JAVACMD=java
- if ! command -v java >/dev/null 2>&1
- then
- die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
-
-Please set the JAVA_HOME variable in your environment to match the
-location of your Java installation."
- fi
-fi
-
-# Increase the maximum file descriptors if we can.
-if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
- case $MAX_FD in #(
- max*)
- # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC2039,SC3045
- MAX_FD=$( ulimit -H -n ) ||
- warn "Could not query maximum file descriptor limit"
- esac
- case $MAX_FD in #(
- '' | soft) :;; #(
- *)
- # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
- # shellcheck disable=SC2039,SC3045
- ulimit -n "$MAX_FD" ||
- warn "Could not set maximum file descriptor limit to $MAX_FD"
- esac
-fi
-
-# Collect all arguments for the java command, stacking in reverse order:
-# * args from the command line
-# * the main class name
-# * -classpath
-# * -D...appname settings
-# * --module-path (only if needed)
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
-
-# For Cygwin or MSYS, switch paths to Windows format before running java
-if "$cygwin" || "$msys" ; then
- APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
- CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
-
- JAVACMD=$( cygpath --unix "$JAVACMD" )
-
- # Now convert the arguments - kludge to limit ourselves to /bin/sh
- for arg do
- if
- case $arg in #(
- -*) false ;; # don't mess with options #(
- /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
- [ -e "$t" ] ;; #(
- *) false ;;
- esac
- then
- arg=$( cygpath --path --ignore --mixed "$arg" )
- fi
- # Roll the args list around exactly as many times as the number of
- # args, so each arg winds up back in the position where it started, but
- # possibly modified.
- #
- # NB: a `for` loop captures its iteration list before it begins, so
- # changing the positional parameters here affects neither the number of
- # iterations, nor the values presented in `arg`.
- shift # remove old arg
- set -- "$@" "$arg" # push replacement arg
- done
-fi
-
-
-# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
-
-# Collect all arguments for the java command:
-# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
-# and any embedded shellness will be escaped.
-# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
-# treated as '${Hostname}' itself on the command line.
-
-set -- \
- "-Dorg.gradle.appname=$APP_BASE_NAME" \
- -classpath "$CLASSPATH" \
- org.gradle.wrapper.GradleWrapperMain \
- "$@"
-
-# Stop when "xargs" is not available.
-if ! command -v xargs >/dev/null 2>&1
-then
- die "xargs is not available"
-fi
-
-# Use "xargs" to parse quoted args.
-#
-# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
-#
-# In Bash we could simply go:
-#
-# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
-# set -- "${ARGS[@]}" "$@"
-#
-# but POSIX shell has neither arrays nor command substitution, so instead we
-# post-process each arg (as a line of input to sed) to backslash-escape any
-# character that might be a shell metacharacter, then use eval to reverse
-# that process (while maintaining the separation between arguments), and wrap
-# the whole thing up as a single "set" statement.
-#
-# This will of course break if any of these variables contains a newline or
-# an unmatched quote.
-#
-
-eval "set -- $(
- printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
- xargs -n1 |
- sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
- tr '\n' ' '
- )" '"$@"'
-
-exec "$JAVACMD" "$@"
diff --git a/capacitor-plugin/android/gradlew.bat b/capacitor-plugin/android/gradlew.bat
deleted file mode 100644
index 9d21a218..00000000
--- a/capacitor-plugin/android/gradlew.bat
+++ /dev/null
@@ -1,94 +0,0 @@
-@rem
-@rem Copyright 2015 the original author or authors.
-@rem
-@rem Licensed under the Apache License, Version 2.0 (the "License");
-@rem you may not use this file except in compliance with the License.
-@rem You may obtain a copy of the License at
-@rem
-@rem https://www.apache.org/licenses/LICENSE-2.0
-@rem
-@rem Unless required by applicable law or agreed to in writing, software
-@rem distributed under the License is distributed on an "AS IS" BASIS,
-@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-@rem See the License for the specific language governing permissions and
-@rem limitations under the License.
-@rem
-@rem SPDX-License-Identifier: Apache-2.0
-@rem
-
-@if "%DEBUG%"=="" @echo off
-@rem ##########################################################################
-@rem
-@rem Gradle startup script for Windows
-@rem
-@rem ##########################################################################
-
-@rem Set local scope for the variables with windows NT shell
-if "%OS%"=="Windows_NT" setlocal
-
-set DIRNAME=%~dp0
-if "%DIRNAME%"=="" set DIRNAME=.
-@rem This is normally unused
-set APP_BASE_NAME=%~n0
-set APP_HOME=%DIRNAME%
-
-@rem Resolve any "." and ".." in APP_HOME to make it shorter.
-for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
-
-@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
-set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
-
-@rem Find java.exe
-if defined JAVA_HOME goto findJavaFromJavaHome
-
-set JAVA_EXE=java.exe
-%JAVA_EXE% -version >NUL 2>&1
-if %ERRORLEVEL% equ 0 goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:findJavaFromJavaHome
-set JAVA_HOME=%JAVA_HOME:"=%
-set JAVA_EXE=%JAVA_HOME%/bin/java.exe
-
-if exist "%JAVA_EXE%" goto execute
-
-echo. 1>&2
-echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
-echo. 1>&2
-echo Please set the JAVA_HOME variable in your environment to match the 1>&2
-echo location of your Java installation. 1>&2
-
-goto fail
-
-:execute
-@rem Setup the command line
-
-set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
-
-
-@rem Execute Gradle
-"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
-
-:end
-@rem End local scope for the variables with windows NT shell
-if %ERRORLEVEL% equ 0 goto mainEnd
-
-:fail
-rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
-rem the _cmd.exe /c_ return code!
-set EXIT_CODE=%ERRORLEVEL%
-if %EXIT_CODE% equ 0 set EXIT_CODE=1
-if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
-exit /b %EXIT_CODE%
-
-:mainEnd
-if "%OS%"=="Windows_NT" endlocal
-
-:omega
diff --git a/capacitor-plugin/android/proguard-rules.pro b/capacitor-plugin/android/proguard-rules.pro
deleted file mode 100644
index f1b42451..00000000
--- a/capacitor-plugin/android/proguard-rules.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-# Add project specific ProGuard rules here.
-# You can control the set of applied configuration files using the
-# proguardFiles setting in build.gradle.
-#
-# For more details, see
-# http://developer.android.com/guide/developing/tools/proguard.html
-
-# If your project uses WebView with JS, uncomment the following
-# and specify the fully qualified class name to the JavaScript interface
-# class:
-#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
-# public *;
-#}
-
-# Uncomment this to preserve the line number information for
-# debugging stack traces.
-#-keepattributes SourceFile,LineNumberTable
-
-# If you keep the line number information, uncomment this to
-# hide the original source file name.
-#-renamesourcefileattribute SourceFile
diff --git a/capacitor-plugin/android/settings.gradle b/capacitor-plugin/android/settings.gradle
deleted file mode 100644
index e558db63..00000000
--- a/capacitor-plugin/android/settings.gradle
+++ /dev/null
@@ -1,2 +0,0 @@
-include ':capacitor-android'
-project(':capacitor-android').projectDir = new File('../../node_modules/@capacitor/android/capacitor')
\ No newline at end of file
diff --git a/capacitor-plugin/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java b/capacitor-plugin/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java
deleted file mode 100644
index 58020e16..00000000
--- a/capacitor-plugin/android/src/androidTest/java/com/getcapacitor/android/ExampleInstrumentedTest.java
+++ /dev/null
@@ -1,26 +0,0 @@
-package com.getcapacitor.android;
-
-import static org.junit.Assert.*;
-
-import android.content.Context;
-import androidx.test.ext.junit.runners.AndroidJUnit4;
-import androidx.test.platform.app.InstrumentationRegistry;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-/**
- * Instrumented test, which will execute on an Android device.
- *
- * @see Testing documentation
- */
-@RunWith(AndroidJUnit4.class)
-public class ExampleInstrumentedTest {
-
- @Test
- public void useAppContext() throws Exception {
- // Context of the app under test.
- Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
-
- assertEquals("com.getcapacitor.android", appContext.getPackageName());
- }
-}
diff --git a/capacitor-plugin/android/src/main/AndroidManifest.xml b/capacitor-plugin/android/src/main/AndroidManifest.xml
deleted file mode 100644
index a2f47b60..00000000
--- a/capacitor-plugin/android/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
diff --git a/capacitor-plugin/android/src/main/java/com/pulse/capacitor/plugin/PulseEditorCapacitor.java b/capacitor-plugin/android/src/main/java/com/pulse/capacitor/plugin/PulseEditorCapacitor.java
deleted file mode 100644
index 4f018709..00000000
--- a/capacitor-plugin/android/src/main/java/com/pulse/capacitor/plugin/PulseEditorCapacitor.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package com.pulse.capacitor.plugin;
-
-import android.util.Log;
-import android.content.Intent;
-import android.app.Activity;
-import android.net.Uri;
-import android.provider.Settings;
-import android.os.Environment;
-
-public class PulseEditorCapacitor {
-
- public String echo(String value) {
- Log.i("Echo", value);
- return value;
- }
-
- public void startManageStorageIntent(Activity activity, String packageName) {
- Intent intent = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
- activity.startActivity(intent);
- }
-
- public boolean isManageStoragePermissionGranted() {
- return Environment.isExternalStorageManager();
- }
-}
diff --git a/capacitor-plugin/android/src/main/java/com/pulse/capacitor/plugin/PulseEditorCapacitorPlugin.java b/capacitor-plugin/android/src/main/java/com/pulse/capacitor/plugin/PulseEditorCapacitorPlugin.java
deleted file mode 100644
index 192caa65..00000000
--- a/capacitor-plugin/android/src/main/java/com/pulse/capacitor/plugin/PulseEditorCapacitorPlugin.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package com.pulse.capacitor.plugin;
-
-import com.getcapacitor.JSObject;
-import com.getcapacitor.Plugin;
-import com.getcapacitor.PluginCall;
-import com.getcapacitor.PluginMethod;
-import com.getcapacitor.annotation.CapacitorPlugin;
-import com.getcapacitor.annotation.Permission;
-import android.app.Activity;
-
-@CapacitorPlugin(
- name = "PulseEditorCapacitor",
- permissions = {
- @Permission(
- alias = "storage",
- strings = {
- "Manifest.permission.MANAGE_EXTERNAL_STORAGE"
- }
- )
- }
-)
-public class PulseEditorCapacitorPlugin extends Plugin {
-
- private PulseEditorCapacitor implementation = new PulseEditorCapacitor();
-
- @PluginMethod
- public void echo(PluginCall call) {
- String value = call.getString("value");
-
- JSObject ret = new JSObject();
- ret.put("value", implementation.echo(value));
- call.resolve(ret);
- }
-
- @PluginMethod
- public void startManageStorageIntent(PluginCall call) {
- Activity activity = getActivity();
- String packageName = activity.getPackageName();
- implementation.startManageStorageIntent(activity, packageName);
- call.resolve();
- }
-
- @PluginMethod
- public void isManageStoragePermissionGranted(PluginCall call) {
- boolean isGranted = implementation.isManageStoragePermissionGranted();
- JSObject ret = new JSObject();
- ret.put("isGranted", isGranted);
- call.resolve(ret);
- }
-}
diff --git a/capacitor-plugin/android/src/main/res/.gitkeep b/capacitor-plugin/android/src/main/res/.gitkeep
deleted file mode 100644
index e69de29b..00000000
diff --git a/capacitor-plugin/android/src/test/java/com/getcapacitor/ExampleUnitTest.java b/capacitor-plugin/android/src/test/java/com/getcapacitor/ExampleUnitTest.java
deleted file mode 100644
index a0fed0cf..00000000
--- a/capacitor-plugin/android/src/test/java/com/getcapacitor/ExampleUnitTest.java
+++ /dev/null
@@ -1,18 +0,0 @@
-package com.getcapacitor;
-
-import static org.junit.Assert.*;
-
-import org.junit.Test;
-
-/**
- * Example local unit test, which will execute on the development machine (host).
- *
- * @see Testing documentation
- */
-public class ExampleUnitTest {
-
- @Test
- public void addition_isCorrect() throws Exception {
- assertEquals(4, 2 + 2);
- }
-}
diff --git a/capacitor-plugin/ios/.gitignore b/capacitor-plugin/ios/.gitignore
deleted file mode 100644
index afb34f83..00000000
--- a/capacitor-plugin/ios/.gitignore
+++ /dev/null
@@ -1,8 +0,0 @@
-.DS_Store
-.build
-/Packages
-xcuserdata/
-DerivedData/
-.swiftpm/configuration/registries.json
-.swiftpm/xcode/package.xcworkspace/contents.xcworkspacedata
-.netrc
\ No newline at end of file
diff --git a/capacitor-plugin/ios/Sources/PulseEditorCapacitorPlugin/PulseEditorCapacitor.swift b/capacitor-plugin/ios/Sources/PulseEditorCapacitorPlugin/PulseEditorCapacitor.swift
deleted file mode 100644
index 8357a184..00000000
--- a/capacitor-plugin/ios/Sources/PulseEditorCapacitorPlugin/PulseEditorCapacitor.swift
+++ /dev/null
@@ -1,8 +0,0 @@
-import Foundation
-
-@objc public class PulseEditorCapacitor: NSObject {
- @objc public func echo(_ value: String) -> String {
- print(value)
- return value
- }
-}
diff --git a/capacitor-plugin/ios/Sources/PulseEditorCapacitorPlugin/PulseEditorCapacitorPlugin.swift b/capacitor-plugin/ios/Sources/PulseEditorCapacitorPlugin/PulseEditorCapacitorPlugin.swift
deleted file mode 100644
index de2208a7..00000000
--- a/capacitor-plugin/ios/Sources/PulseEditorCapacitorPlugin/PulseEditorCapacitorPlugin.swift
+++ /dev/null
@@ -1,23 +0,0 @@
-import Foundation
-import Capacitor
-
-/**
- * Please read the Capacitor iOS Plugin Development Guide
- * here: https://capacitorjs.com/docs/plugins/ios
- */
-@objc(PulseEditorCapacitorPlugin)
-public class PulseEditorCapacitorPlugin: CAPPlugin, CAPBridgedPlugin {
- public let identifier = "PulseEditorCapacitorPlugin"
- public let jsName = "PulseEditorCapacitor"
- public let pluginMethods: [CAPPluginMethod] = [
- CAPPluginMethod(name: "echo", returnType: CAPPluginReturnPromise)
- ]
- private let implementation = PulseEditorCapacitor()
-
- @objc func echo(_ call: CAPPluginCall) {
- let value = call.getString("value") ?? ""
- call.resolve([
- "value": implementation.echo(value)
- ])
- }
-}
diff --git a/capacitor-plugin/ios/Tests/PulseEditorCapacitorPluginTests/PulseEditorCapacitorPluginTests.swift b/capacitor-plugin/ios/Tests/PulseEditorCapacitorPluginTests/PulseEditorCapacitorPluginTests.swift
deleted file mode 100644
index 4b07cc5c..00000000
--- a/capacitor-plugin/ios/Tests/PulseEditorCapacitorPluginTests/PulseEditorCapacitorPluginTests.swift
+++ /dev/null
@@ -1,15 +0,0 @@
-import XCTest
-@testable import PulseEditorCapacitorPlugin
-
-class PulseEditorCapacitorTests: XCTestCase {
- func testEcho() {
- // This is an example of a functional test case for a plugin.
- // Use XCTAssert and related functions to verify your tests produce the correct results.
-
- let implementation = PulseEditorCapacitor()
- let value = "Hello, World!"
- let result = implementation.echo(value)
-
- XCTAssertEqual(value, result)
- }
-}
diff --git a/capacitor-plugin/package.json b/capacitor-plugin/package.json
deleted file mode 100644
index 7406e126..00000000
--- a/capacitor-plugin/package.json
+++ /dev/null
@@ -1,81 +0,0 @@
-{
- "name": "@pulse-editor/capacitor-plugin",
- "version": "0.0.1",
- "private": true,
- "description": "Capacitor plugins for Pulse Editor",
- "main": "dist/plugin.cjs.js",
- "module": "dist/esm/index.js",
- "types": "dist/esm/index.d.ts",
- "unpkg": "dist/plugin.js",
- "files": [
- "android/src/main/",
- "android/build.gradle",
- "dist/",
- "ios/Sources",
- "ios/Tests",
- "Package.swift",
- "PulseEditorCapacitorPlugin.podspec"
- ],
- "author": "ClayPulse",
- "license": "MIT",
- "repository": {
- "type": "git",
- "url": "git+https://github.com/ClayPulse/pulse-editor.git"
- },
- "bugs": {
- "url": "https://github.com/ClayPulse/pulse-editor/issues"
- },
- "keywords": [
- "capacitor",
- "plugin",
- "native"
- ],
- "scripts": {
- "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
- "verify:ios": "xcodebuild -scheme PulseEditorCapacitorPlugin -destination generic/platform=iOS",
- "verify:android": "cd android && gradlew clean build test && cd ..",
- "verify:web": "npm run build",
- "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
- "fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
- "eslint": "eslint . --ext ts",
- "prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
- "swiftlint": "node-swiftlint",
- "docgen": "docgen --api PulseEditorCapacitorPlugin --output-readme README.md --output-json dist/docs.json",
- "build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
- "clean": "rimraf ./dist",
- "watch": "tsc --watch",
- "prepublishOnly": "npm run build"
- },
- "devDependencies": {
- "@capacitor/android": "^7.0.0",
- "@capacitor/core": "^7.0.0",
- "@capacitor/docgen": "^0.3.0",
- "@capacitor/ios": "^7.0.0",
- "@ionic/eslint-config": "^0.4.0",
- "@ionic/prettier-config": "^4.0.0",
- "@ionic/swiftlint-config": "^2.0.0",
- "eslint": "^8.57.0",
- "prettier": "^3.4.2",
- "prettier-plugin-java": "^2.6.6",
- "rimraf": "^6.0.1",
- "rollup": "^4.30.1",
- "swiftlint": "^2.0.0",
- "typescript": "^5.8.3"
- },
- "peerDependencies": {
- "@capacitor/core": ">=7.0.0"
- },
- "prettier": "@ionic/prettier-config",
- "swiftlint": "@ionic/swiftlint-config",
- "eslintConfig": {
- "extends": "@ionic/eslint-config/recommended"
- },
- "capacitor": {
- "ios": {
- "src": "ios"
- },
- "android": {
- "src": "android"
- }
- }
-}
diff --git a/capacitor-plugin/rollup.config.mjs b/capacitor-plugin/rollup.config.mjs
deleted file mode 100644
index 8cc2a1a1..00000000
--- a/capacitor-plugin/rollup.config.mjs
+++ /dev/null
@@ -1,22 +0,0 @@
-export default {
- input: 'dist/esm/index.js',
- output: [
- {
- file: 'dist/plugin.js',
- format: 'iife',
- name: 'capacitorPulseEditorCapacitor',
- globals: {
- '@capacitor/core': 'capacitorExports',
- },
- sourcemap: true,
- inlineDynamicImports: true,
- },
- {
- file: 'dist/plugin.cjs.js',
- format: 'cjs',
- sourcemap: true,
- inlineDynamicImports: true,
- },
- ],
- external: ['@capacitor/core'],
-};
diff --git a/capacitor-plugin/src/definitions.ts b/capacitor-plugin/src/definitions.ts
deleted file mode 100644
index 0a3941f8..00000000
--- a/capacitor-plugin/src/definitions.ts
+++ /dev/null
@@ -1,5 +0,0 @@
-export interface PulseEditorCapacitorPlugin {
- echo(options: { value: string }): Promise<{ value: string }>;
- startManageStorageIntent(): Promise;
- isManageStoragePermissionGranted(): Promise<{ isGranted: boolean }>;
-}
diff --git a/capacitor-plugin/src/index.ts b/capacitor-plugin/src/index.ts
deleted file mode 100644
index 8ea4deb7..00000000
--- a/capacitor-plugin/src/index.ts
+++ /dev/null
@@ -1,10 +0,0 @@
-import { registerPlugin } from '@capacitor/core';
-
-import type { PulseEditorCapacitorPlugin } from './definitions';
-
-const PulseEditorCapacitor = registerPlugin('PulseEditorCapacitor', {
- web: () => import('./web').then((m) => new m.PulseEditorCapacitorWeb()),
-});
-
-export * from './definitions';
-export { PulseEditorCapacitor };
diff --git a/capacitor-plugin/src/web.ts b/capacitor-plugin/src/web.ts
deleted file mode 100644
index 9b31b7ef..00000000
--- a/capacitor-plugin/src/web.ts
+++ /dev/null
@@ -1,18 +0,0 @@
-import { WebPlugin } from '@capacitor/core';
-
-import type { PulseEditorCapacitorPlugin } from './definitions';
-
-export class PulseEditorCapacitorWeb extends WebPlugin implements PulseEditorCapacitorPlugin {
- async echo(options: { value: string }): Promise<{ value: string }> {
- console.log('ECHO', options);
- return options;
- }
-
- async startManageStorageIntent(): Promise {
- throw new Error('Method not implemented.');
- }
-
- async isManageStoragePermissionGranted(): Promise<{ isGranted: boolean }> {
- throw new Error('Method not implemented.');
- }
-}
diff --git a/capacitor-plugin/tsconfig.json b/capacitor-plugin/tsconfig.json
deleted file mode 100644
index f2e88e6a..00000000
--- a/capacitor-plugin/tsconfig.json
+++ /dev/null
@@ -1,20 +0,0 @@
-{
- "compilerOptions": {
- "allowUnreachableCode": false,
- "declaration": true,
- "esModuleInterop": true,
- "inlineSources": true,
- "lib": ["dom", "es2017"],
- "module": "esnext",
- "moduleResolution": "node",
- "noFallthroughCasesInSwitch": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "outDir": "dist/esm",
- "pretty": true,
- "sourceMap": true,
- "strict": true,
- "target": "es2017"
- },
- "files": ["src/index.ts"]
-}
diff --git a/desktop/lib/node-pty-server.js b/desktop/lib/node-pty-server.js
index 0296b9ae..7f36d5df 100644
--- a/desktop/lib/node-pty-server.js
+++ b/desktop/lib/node-pty-server.js
@@ -37,7 +37,7 @@ const handleTerminalConnection = (ws) => {
}
});
- ptyProcess.on("data", (rawOutput) => {
+ ptyProcess.onData((rawOutput) => {
ws.send(JSON.stringify({ type: "output", payload: rawOutput }));
});
@@ -68,4 +68,6 @@ export function createTerminalServer() {
server.listen(port, () => {
console.log(`HTTP and WebSocket server is running on port ${port}`);
});
+
+ return server;
}
diff --git a/desktop/main.mjs b/desktop/main.mjs
index 8cf17e1f..c9f8a2ee 100644
--- a/desktop/main.mjs
+++ b/desktop/main.mjs
@@ -35,6 +35,8 @@ serve({
});
let mainWindow = null;
+let sharedSession = null;
+let terminalServer = null;
function createMainWindow() {
const win = new BrowserWindow({
@@ -42,6 +44,7 @@ function createMainWindow() {
height: 600,
webPreferences: {
preload: path.join(__dirname, "preload.mjs"),
+ session: sharedSession,
},
titleBarOverlay: {
color: "#00000000",
@@ -277,29 +280,39 @@ async function handleLogin(event) {
webPreferences: {
nodeIntegration: false,
contextIsolation: true,
- session: session.defaultSession, // ← important
+ // login window and main window will automatically share cookies — no manual copying needed:
+ session: sharedSession, // ← important
},
});
- const signinUrl = "https://pulse-editor.com/api/auth/signin";
+ const signinUrl = app.isPackaged
+ ? "https://pulse-editor.com/api/auth/signin"
+ : "https://localhost:8080/api/auth/signin";
await loginWindow.loadURL(signinUrl);
const loginSession = loginWindow.webContents.session;
+ // clear login window cookies before starting login
+ await clearLoginCookies(loginSession);
+
const interval = setInterval(async () => {
try {
- const cookies = await loginSession.cookies.get({ name: cookieName });
+ const cookies = await sharedSession.cookies.get({ name: cookieName });
+
+ // If the cookie is present, reload the main window to reflect logged-in state
if (cookies.length > 0) {
clearInterval(interval);
+ mainWindow.reload();
+
+ // Check if cookies are present in the main window session
+ const mainCookies = await sharedSession.cookies.get({
+ name: cookieName,
+ });
+
// Login successful
loginWindow.close();
console.log(`Login successful, cookie "${cookieName}" present.`);
-
- // Reload main window
- if (mainWindow) {
- mainWindow.reload();
- }
}
} catch (err) {
console.error("Error checking cookie:", err);
@@ -308,21 +321,8 @@ async function handleLogin(event) {
}
async function handleLogout() {
- const mainSession = session.defaultSession;
- const cookieName = "pulse-editor.session-token";
-
try {
- const cookies = await mainSession.cookies.get({ name: cookieName });
-
- for (const cookie of cookies) {
- const url = cookie.domain.startsWith(".")
- ? `https://${cookie.domain.slice(1)}${cookie.path}`
- : `https://${cookie.domain}${cookie.path}`;
-
- await mainSession.cookies.remove(url, cookie.name);
- }
-
- console.log(`Cookie "${cookieName}" removed. Logout successful.`);
+ await clearLoginCookies(sharedSession);
// Reload main window
if (mainWindow) {
@@ -337,10 +337,34 @@ async function handleLogout() {
}
}
+async function clearLoginCookies(currentSession) {
+ // await currentSession.clearStorageData({ storages: ["cookies"] });
+
+ const cookieName = "pulse-editor.session-token";
+
+ const cookies = await currentSession.cookies.get({
+ name: cookieName,
+ });
+
+ for (const cookie of cookies) {
+ // Determine the scheme (secure cookies use https)
+ const protocol = cookie.secure ? "https://" : "http://";
+
+ // Build the URL using domain and path
+ // Note: cookie.domain may start with a leading dot, remove it
+ const domain = cookie.domain.startsWith(".")
+ ? cookie.domain.slice(1)
+ : cookie.domain;
+
+ const url = `${protocol}${domain}${cookie.path}`;
+ await currentSession.cookies.remove(url, cookie.name);
+ }
+}
+
let isCreatedTerminal = false;
function handleCreateTerminal(event) {
if (!isCreatedTerminal) {
- createTerminalServer();
+ terminalServer = createTerminalServer();
isCreatedTerminal = true;
}
@@ -348,6 +372,9 @@ function handleCreateTerminal(event) {
}
app.whenReady().then(() => {
+ sharedSession = session.defaultSession;
+ console.log("Shared session path:", sharedSession.storagePath);
+
ipcMain.handle("select-dir", handleSelectDir);
ipcMain.handle("select-file", handleSelectFile);
@@ -370,8 +397,8 @@ app.whenReady().then(() => {
ipcMain.handle("copy-files", handleCopyFiles);
- ipcMain.handle("load-settings", handleLoadSettings);
- ipcMain.handle("save-settings", handleSaveSettings);
+ ipcMain.handle("get-persistent-settings", handleLoadSettings);
+ ipcMain.handle("set-persistent-settings", handleSaveSettings);
ipcMain.handle("get-installation-path", handleGetInstallationPath);
@@ -387,4 +414,9 @@ app.on("window-all-closed", () => {
if (process.platform !== "darwin") {
app.quit();
}
+
+ // Close terminal server if running
+ if (isCreatedTerminal) {
+ terminalServer.close();
+ }
});
diff --git a/desktop/package-lock.json b/desktop/package-lock.json
index c992641e..aca031c9 100644
--- a/desktop/package-lock.json
+++ b/desktop/package-lock.json
@@ -12,7 +12,7 @@
"fs-extra": "^7.0.1",
"ignore": "^5.3.2",
"node-addon-api": "^7.1.1",
- "node-pty": "^1.1.0-beta34",
+ "node-pty": "^1.1.0-beta37",
"ws": "^8.18.2"
},
"devDependencies": {
@@ -4256,9 +4256,9 @@
}
},
"node_modules/node-pty": {
- "version": "1.1.0-beta9",
- "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta9.tgz",
- "integrity": "sha512-/Ue38pvXJdgRZ3+me1FgfglLd301GhJN0NStiotdt61tm43N5htUyR/IXOUzOKuNaFmCwIhy6nwb77Ky41LMbw==",
+ "version": "1.1.0-beta37",
+ "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta37.tgz",
+ "integrity": "sha512-Ys8AW98Atyu9cLV5QLQshvSTF+YMDksVi2ULkYNPAKLGzaDQbXuOEjStg4ZZuL4c8saOTh1+2PNlCoyuagBr1Q==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/desktop/package.json b/desktop/package.json
index a123f5c3..205170ad 100644
--- a/desktop/package.json
+++ b/desktop/package.json
@@ -15,7 +15,7 @@
"fs-extra": "^7.0.1",
"ignore": "^5.3.2",
"node-addon-api": "^7.1.1",
- "node-pty": "^1.1.0-beta34",
+ "node-pty": "^1.1.0-beta37",
"ws": "^8.18.2"
},
"devDependencies": {
diff --git a/desktop/preload.mjs b/desktop/preload.mjs
index 2f49128d..ab56b490 100644
--- a/desktop/preload.mjs
+++ b/desktop/preload.mjs
@@ -1,6 +1,7 @@
const { contextBridge, ipcRenderer } = require("electron");
contextBridge.exposeInMainWorld("electronAPI", {
+ // #region Platform API
selectDir: () => ipcRenderer.invoke("select-dir"),
selectFile: (fileExtension) =>
ipcRenderer.invoke("select-file", fileExtension),
@@ -25,13 +26,17 @@ contextBridge.exposeInMainWorld("electronAPI", {
copyFiles: (from, to) => ipcRenderer.invoke("copy-files", from, to),
- loadSettings: () => ipcRenderer.invoke("load-settings"),
- saveSettings: (settings) => ipcRenderer.invoke("save-settings", settings),
+ getPersistentSettings: () => ipcRenderer.invoke("get-persistent-settings"),
+ setPersistentSettings: (settings) => ipcRenderer.invoke("set-persistent-settings", settings),
getInstallationPath: () => ipcRenderer.invoke("get-installation-path"),
createTerminal: () => ipcRenderer.invoke("create-terminal"),
+ // #endregion
+
+ // #region Auth API
login: () => ipcRenderer.invoke("login"),
logout: () => ipcRenderer.invoke("logout"),
+ // #endregion
});
diff --git a/mobile/android/.gitignore b/mobile/android/.gitignore
index 7d82a7ca..89399e18 100644
--- a/mobile/android/.gitignore
+++ b/mobile/android/.gitignore
@@ -56,7 +56,7 @@ captures/
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
-#*.keystore
+*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
diff --git a/mobile/android/app/build.gradle b/mobile/android/app/build.gradle
index 9ae01b72..73b79861 100644
--- a/mobile/android/app/build.gradle
+++ b/mobile/android/app/build.gradle
@@ -8,7 +8,7 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode 1
- versionName "v0.0.1-alpha"
+ versionName "v0.1.1-beta"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
aaptOptions {
// Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps.
diff --git a/mobile/android/app/capacitor.build.gradle b/mobile/android/app/capacitor.build.gradle
index 6d702635..f80517b2 100644
--- a/mobile/android/app/capacitor.build.gradle
+++ b/mobile/android/app/capacitor.build.gradle
@@ -10,12 +10,14 @@ android {
apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle"
dependencies {
implementation project(':capacitor-community-safe-area')
+ implementation project(':capacitor-app')
+ implementation project(':capacitor-browser')
implementation project(':capacitor-filesystem')
implementation project(':capacitor-keyboard')
+ implementation project(':capacitor-preferences')
implementation project(':capacitor-screen-orientation')
implementation project(':capacitor-status-bar')
implementation project(':capawesome-capacitor-file-picker')
- implementation project(':pulse-editor-capacitor-plugin')
}
diff --git a/mobile/android/app/src/main/AndroidManifest.xml b/mobile/android/app/src/main/AndroidManifest.xml
index 479d6703..c60fa1c4 100644
--- a/mobile/android/app/src/main/AndroidManifest.xml
+++ b/mobile/android/app/src/main/AndroidManifest.xml
@@ -1,4 +1,4 @@
-
+
@@ -10,7 +10,7 @@
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme"
- android:usesCleartextTraffic="true">
+ android:networkSecurityConfig="@xml/network_security_config">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
+
@@ -43,7 +60,4 @@
-
-
-
+
\ No newline at end of file
diff --git a/mobile/android/app/src/main/res/xml/network_security_config.xml b/mobile/android/app/src/main/res/xml/network_security_config.xml
index de61259a..bb6ab93d 100644
--- a/mobile/android/app/src/main/res/xml/network_security_config.xml
+++ b/mobile/android/app/src/main/res/xml/network_security_config.xml
@@ -1,6 +1,9 @@
-
- localhost
-
-
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/mobile/android/capacitor.settings.gradle b/mobile/android/capacitor.settings.gradle
index f2bf9f46..d224628d 100644
--- a/mobile/android/capacitor.settings.gradle
+++ b/mobile/android/capacitor.settings.gradle
@@ -3,7 +3,13 @@ include ':capacitor-android'
project(':capacitor-android').projectDir = new File('../../node_modules/@capacitor/android/capacitor')
include ':capacitor-community-safe-area'
-project(':capacitor-community-safe-area').projectDir = new File('../../node_modules/@capacitor-community/safe-area/android')
+project(':capacitor-community-safe-area').projectDir = new File('../node_modules/@capacitor-community/safe-area/android')
+
+include ':capacitor-app'
+project(':capacitor-app').projectDir = new File('../../node_modules/@capacitor/app/android')
+
+include ':capacitor-browser'
+project(':capacitor-browser').projectDir = new File('../../node_modules/@capacitor/browser/android')
include ':capacitor-filesystem'
project(':capacitor-filesystem').projectDir = new File('../../node_modules/@capacitor/filesystem/android')
@@ -11,6 +17,9 @@ project(':capacitor-filesystem').projectDir = new File('../../node_modules/@capa
include ':capacitor-keyboard'
project(':capacitor-keyboard').projectDir = new File('../../node_modules/@capacitor/keyboard/android')
+include ':capacitor-preferences'
+project(':capacitor-preferences').projectDir = new File('../../node_modules/@capacitor/preferences/android')
+
include ':capacitor-screen-orientation'
project(':capacitor-screen-orientation').projectDir = new File('../../node_modules/@capacitor/screen-orientation/android')
@@ -19,6 +28,3 @@ project(':capacitor-status-bar').projectDir = new File('../../node_modules/@capa
include ':capawesome-capacitor-file-picker'
project(':capawesome-capacitor-file-picker').projectDir = new File('../../node_modules/@capawesome/capacitor-file-picker/android')
-
-include ':pulse-editor-capacitor-plugin'
-project(':pulse-editor-capacitor-plugin').projectDir = new File('../../capacitor-plugin/android')
diff --git a/mobile/capacitor.config.ts b/mobile/capacitor.config.ts
index a017e10b..9110b9d4 100644
--- a/mobile/capacitor.config.ts
+++ b/mobile/capacitor.config.ts
@@ -17,6 +17,9 @@ const config: CapacitorConfig = {
SafeArea: {
enabled: true,
},
+ CapacitorHttp: {
+ enabled: true,
+ },
},
};
diff --git a/mobile/package.json b/mobile/package.json
index b32ff734..48b95d96 100644
--- a/mobile/package.json
+++ b/mobile/package.json
@@ -5,18 +5,20 @@
"type": "module",
"scripts": {
"android-dev": "node dev.js",
- "android-build": "npx cap run android"
+ "android-run": "npx cap run android"
},
"dependencies": {
"@capacitor-community/safe-area": "^7.0.0-alpha.1",
"@capacitor/android": "^7.2.0",
+ "@capacitor/app": "^7.1.0",
+ "@capacitor/browser": "^7.0.2",
"@capacitor/cli": "^7.2.0",
"@capacitor/core": "^7.2.0",
"@capacitor/filesystem": "^7.0.1",
"@capacitor/keyboard": "^7.0.1",
+ "@capacitor/preferences": "^7.0.2",
"@capacitor/screen-orientation": "^7.0.1",
"@capacitor/status-bar": "^7.0.1",
- "@capawesome/capacitor-file-picker": "^7.0.1",
- "@pulse-editor/capacitor-plugin": "file:../capacitor-plugin"
+ "@capawesome/capacitor-file-picker": "^7.0.1"
}
}
diff --git a/npm-packages/cli/package.json b/npm-packages/cli/package.json
index 46768f7a..dc20f3bb 100644
--- a/npm-packages/cli/package.json
+++ b/npm-packages/cli/package.json
@@ -1,9 +1,9 @@
{
"name": "@pulse-editor/cli",
- "version": "0.1.0-beta.5",
+ "version": "0.1.0-beta.8",
"license": "MIT",
"bin": {
- "pulse": "./dist/cli.js"
+ "pulse": "dist/cli.js"
},
"type": "module",
"engines": {
@@ -60,4 +60,4 @@
}
},
"prettier": "@vdemedes/prettier-config"
-}
\ No newline at end of file
+}
diff --git a/npm-packages/cli/source/components/commands/create.tsx b/npm-packages/cli/source/components/commands/create.tsx
index 8ae10632..7f9ac339 100644
--- a/npm-packages/cli/source/components/commands/create.tsx
+++ b/npm-packages/cli/source/components/commands/create.tsx
@@ -165,8 +165,9 @@ export default function Create({cli}: {cli: Result}) {
try {
await execa(`npm install`, {
cwd: path.join(process.cwd(), name),
+ shell: true,
});
- } catch (error) {
+ } catch (error: any) {
setCreateMessage(
❌ Failed to install dependencies. Please check your internet
diff --git a/npm-packages/cli/source/components/commands/publish.tsx b/npm-packages/cli/source/components/commands/publish.tsx
index e364175d..b3b43f5f 100644
--- a/npm-packages/cli/source/components/commands/publish.tsx
+++ b/npm-packages/cli/source/components/commands/publish.tsx
@@ -54,13 +54,23 @@ export default function Publish({cli}: {cli: Result}) {
// Build the extension
useEffect(() => {
async function buildExtension() {
+ setIsBuilding(true);
try {
- setIsBuilding(true);
await $`npm run build`;
- // Zip the dist folder
+ }
+ catch (error) {
+ setIsBuildingError(true);
+ setIsBuilding(false);
+ setFailureMessage('Build failed. Please run `npm run build` to see the error.');
+ return;
+ }
+ // Zip the dist folder
+ try {
await $({cwd: 'dist'})`zip -r ../node_modules/@pulse-editor/dist.zip *`;
} catch (error) {
setIsBuildingError(true);
+ setIsBuilding(false);
+ setFailureMessage('Failed to zip the build output.');
return;
} finally {
setIsBuilding(false);
@@ -93,8 +103,8 @@ export default function Publish({cli}: {cli: Result}) {
// Send the file to the server
const res = await fetch(
cli.flags.dev
- ? 'https://localhost:8080/api/extension/publish'
- : 'https://pulse-editor.com/api/extension/publish',
+ ? 'https://localhost:8080/api/app/publish'
+ : 'https://pulse-editor.com/api/app/publish',
{
method: 'POST',
headers: {
diff --git a/package-lock.json b/package-lock.json
index 91cd85a4..90bb5aa7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -10,7 +10,6 @@
"workspaces": [
"web",
"mobile",
- "capacitor-plugin",
"vscode-extension",
"npm-packages/react-api",
"npm-packages/shared-utils"
@@ -25,6 +24,7 @@
"capacitor-plugin": {
"name": "@pulse-editor/capacitor-plugin",
"version": "0.0.1",
+ "extraneous": true,
"license": "MIT",
"devDependencies": {
"@capacitor/android": "^7.0.0",
@@ -46,90 +46,22 @@
"@capacitor/core": ">=7.0.0"
}
},
- "capacitor-plugin/node_modules/glob": {
- "version": "11.0.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz",
- "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "foreground-child": "^3.3.1",
- "jackspeak": "^4.1.1",
- "minimatch": "^10.0.3",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^2.0.0"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": "20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "capacitor-plugin/node_modules/minimatch": {
- "version": "10.0.3",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz",
- "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "@isaacs/brace-expansion": "^5.0.0"
- },
- "engines": {
- "node": "20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "capacitor-plugin/node_modules/prettier-plugin-java": {
- "version": "2.6.7",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "java-parser": "2.3.3",
- "lodash": "4.17.21"
- },
- "peerDependencies": {
- "prettier": "^3.0.0"
- }
- },
- "capacitor-plugin/node_modules/rimraf": {
- "version": "6.0.1",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "glob": "^11.0.0",
- "package-json-from-dist": "^1.0.0"
- },
- "bin": {
- "rimraf": "dist/esm/bin.mjs"
- },
- "engines": {
- "node": "20 || >=22"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"mobile": {
"name": "@pulse-editor/mobile",
"version": "0.0.1",
"dependencies": {
"@capacitor-community/safe-area": "^7.0.0-alpha.1",
"@capacitor/android": "^7.2.0",
+ "@capacitor/app": "^7.1.0",
+ "@capacitor/browser": "^7.0.2",
"@capacitor/cli": "^7.2.0",
"@capacitor/core": "^7.2.0",
"@capacitor/filesystem": "^7.0.1",
"@capacitor/keyboard": "^7.0.1",
+ "@capacitor/preferences": "^7.0.2",
"@capacitor/screen-orientation": "^7.0.1",
"@capacitor/status-bar": "^7.0.1",
- "@capawesome/capacitor-file-picker": "^7.0.1",
- "@pulse-editor/capacitor-plugin": "file:../capacitor-plugin"
+ "@capawesome/capacitor-file-picker": "^7.0.1"
}
},
"mobile/node_modules/@capacitor-community/safe-area": {
@@ -1830,6 +1762,24 @@
"@capacitor/core": "^7.4.0"
}
},
+ "node_modules/@capacitor/app": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/@capacitor/app/-/app-7.1.0.tgz",
+ "integrity": "sha512-W7m09IWrUjZbo7AKeq+rc/KyucxrJekTBg0l4QCm/yDtCejE3hebxp/W2esU26KKCzMc7H3ClkUw32E9lZkwRA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@capacitor/core": ">=7.0.0"
+ }
+ },
+ "node_modules/@capacitor/browser": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@capacitor/browser/-/browser-7.0.2.tgz",
+ "integrity": "sha512-5kySTunCtH+2sezmTjgDfwvspW7GW/hslQECZeLIRM2qefnxjGTc3fmCTeILYK5EuvcxMs+8sF5BhmzzKqOzuQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@capacitor/core": ">=7.0.0"
+ }
+ },
"node_modules/@capacitor/cli": {
"version": "7.4.3",
"resolved": "https://registry.npmjs.org/@capacitor/cli/-/cli-7.4.3.tgz",
@@ -1963,47 +1913,6 @@
"tslib": "^2.1.0"
}
},
- "node_modules/@capacitor/docgen": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@capacitor/docgen/-/docgen-0.3.0.tgz",
- "integrity": "sha512-WPggobo5Ql70F+2xOIUwNSApJXaL9F/9+Al6B+sNuSAmcg484OAksyUPKgiynF4BVlxeY5a0sDkgdVkmmA3ElQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "^14.18.0",
- "colorette": "^2.0.20",
- "github-slugger": "^1.5.0",
- "minimist": "^1.2.8",
- "typescript": "~4.2.4"
- },
- "bin": {
- "docgen": "bin/docgen"
- },
- "engines": {
- "node": ">=18.0.0"
- }
- },
- "node_modules/@capacitor/docgen/node_modules/@types/node": {
- "version": "14.18.63",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-14.18.63.tgz",
- "integrity": "sha512-fAtCfv4jJg+ExtXhvCkCqUKZ+4ok/JQk01qDKhL5BDDoS3AxKXhV5/MAVUZyQnSEd2GT92fkgZl0pz0Q0AzcIQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@capacitor/docgen/node_modules/typescript": {
- "version": "4.2.4",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.2.4.tgz",
- "integrity": "sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg==",
- "dev": true,
- "license": "Apache-2.0",
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=4.2.0"
- }
- },
"node_modules/@capacitor/filesystem": {
"version": "7.1.4",
"resolved": "https://registry.npmjs.org/@capacitor/filesystem/-/filesystem-7.1.4.tgz",
@@ -2016,16 +1925,6 @@
"@capacitor/core": ">=7.0.0"
}
},
- "node_modules/@capacitor/ios": {
- "version": "7.4.3",
- "resolved": "https://registry.npmjs.org/@capacitor/ios/-/ios-7.4.3.tgz",
- "integrity": "sha512-VNm7cHODgh3KK/4ZC2rXU9gBlvHii/mYFLI+XMXwq24nhB679QxHhz+pUuI7PatYoM2q4MAL0NR/dRgehKCaSA==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "@capacitor/core": "^7.4.0"
- }
- },
"node_modules/@capacitor/keyboard": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@capacitor/keyboard/-/keyboard-7.0.3.tgz",
@@ -2035,6 +1934,15 @@
"@capacitor/core": ">=7.0.0"
}
},
+ "node_modules/@capacitor/preferences": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@capacitor/preferences/-/preferences-7.0.2.tgz",
+ "integrity": "sha512-JVCy0/oc6RsRencLOZ8rMqjNxAlHs7awPJU/MXqangsJ48oO2PnYGHfCvci6WgIJlqyC0QhvWZaO1BR1lVkHWQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@capacitor/core": ">=7.0.0"
+ }
+ },
"node_modules/@capacitor/screen-orientation": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@capacitor/screen-orientation/-/screen-orientation-7.0.2.tgz",
@@ -4764,39 +4672,6 @@
"node": ">=16.0.0"
}
},
- "node_modules/@ionic/eslint-config": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/@ionic/eslint-config/-/eslint-config-0.4.0.tgz",
- "integrity": "sha512-L8OXY29D3iGqNtteFj0iz3eoZIVgokBiVjCO8WMssNZa4GTHjYsase0rC9ASXGefMnLJu6rbNl3Gbx7NNxJRZQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/eslint-plugin": "^5.58.0",
- "@typescript-eslint/parser": "^5.58.0",
- "eslint-config-prettier": "^8.8.0",
- "eslint-plugin-import": "^2.27.0"
- },
- "peerDependencies": {
- "eslint": ">=7"
- }
- },
- "node_modules/@ionic/prettier-config": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/@ionic/prettier-config/-/prettier-config-4.0.0.tgz",
- "integrity": "sha512-0DqL6CggVdgeJAWOLPUT73rF1VD5p0tVlCpC5GXz5vTIUBxNwsJ5085Q7wXjKiE5Odx3aOHGTcuRWCawFsLFag==",
- "dev": true,
- "license": "MIT",
- "peerDependencies": {
- "prettier": "^2.4.0 || ^3.0.0"
- }
- },
- "node_modules/@ionic/swiftlint-config": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@ionic/swiftlint-config/-/swiftlint-config-2.0.0.tgz",
- "integrity": "sha512-TXy76ALSKhUZzBziHz7aoEtSQwHofBIDRNzM9x4sndtC7fefbZsBw3UgGwXFTOc7hoj72EAGyqZNUhj9LlhaNQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@ionic/utils-array": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/@ionic/utils-array/-/utils-array-2.1.6.tgz",
@@ -6290,10 +6165,6 @@
"integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
"license": "BSD-3-Clause"
},
- "node_modules/@pulse-editor/capacitor-plugin": {
- "resolved": "capacitor-plugin",
- "link": true
- },
"node_modules/@pulse-editor/mobile": {
"resolved": "mobile",
"link": true
@@ -11313,33 +11184,6 @@
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
"license": "MIT"
},
- "node_modules/cosmiconfig": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
- "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "env-paths": "^2.2.1",
- "import-fresh": "^3.3.0",
- "js-yaml": "^4.1.0",
- "parse-json": "^5.2.0"
- },
- "engines": {
- "node": ">=14"
- },
- "funding": {
- "url": "https://github.com/sponsors/d-fischer"
- },
- "peerDependencies": {
- "typescript": ">=4.9.5"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
"node_modules/cross-fetch": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.2.0.tgz",
@@ -11946,23 +11790,6 @@
"node": ">=4"
}
},
- "node_modules/error-ex": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
- "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-arrayish": "^0.2.1"
- }
- },
- "node_modules/error-ex/node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/es-abstract": {
"version": "1.24.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz",
@@ -12285,19 +12112,6 @@
}
}
},
- "node_modules/eslint-config-prettier": {
- "version": "8.10.2",
- "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-8.10.2.tgz",
- "integrity": "sha512-/IGJ6+Dka158JnP5n5YFMOszjDWrXggGz1LaK/guZq9vZTmniaKlHcsscvkAhn9y4U+BU3JuUdYvtAMcv30y4A==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "eslint-config-prettier": "bin/cli.js"
- },
- "peerDependencies": {
- "eslint": ">=7.0.0"
- }
- },
"node_modules/eslint-import-resolver-node": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
@@ -13351,13 +13165,6 @@
"url": "https://github.com/privatenumber/get-tsconfig?sponsor=1"
}
},
- "node_modules/github-slugger": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-1.5.0.tgz",
- "integrity": "sha512-wIh+gKBI9Nshz2o46B0B3f5k/W+WI9ZAv6y5Dn5WJ5SK1t0TnDimB4WE5rmTD05ZAIn8HALCZVmCsvj0w0v0lw==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@@ -15623,13 +15430,6 @@
"url": "https://opencollective.com/parcel"
}
},
- "node_modules/lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/loader-runner": {
"version": "4.3.1",
"resolved": "https://registry.npmjs.org/loader-runner/-/loader-runner-4.3.1.tgz",
@@ -20668,25 +20468,6 @@
"integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
"license": "MIT"
},
- "node_modules/parse-json": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
- "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@babel/code-frame": "^7.0.0",
- "error-ex": "^1.3.1",
- "json-parse-even-better-errors": "^2.3.0",
- "lines-and-columns": "^1.1.6"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/path-exists": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -23161,22 +22942,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/swiftlint": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/swiftlint/-/swiftlint-2.0.0.tgz",
- "integrity": "sha512-MMVuyZ4/6WcIJlk0z6GM0pZjRuwnyUJqRPbJBFW3oACN/qjAvRbolCWEu+zE2MycF/cEgqfUpI+oLECNfjfOJA==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "@ionic/utils-fs": "^3.1.7",
- "@ionic/utils-subprocess": "^3.0.1",
- "cosmiconfig": "^9.0.0"
- },
- "bin": {
- "node-swiftlint": "bin.js"
- }
- },
"node_modules/swr": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/swr/-/swr-2.3.6.tgz",
@@ -26522,14 +26287,17 @@
},
"web": {
"name": "@pulse-editor/web",
- "version": "0.1.1-alpha.13",
+ "version": "0.1.1-beta.0",
"dependencies": {
"@capacitor-community/safe-area": "^7.0.0-alpha.1",
"@capacitor/android": "^7.4.3",
+ "@capacitor/app": "^7.1.0",
+ "@capacitor/browser": "^7.0.2",
"@capacitor/cli": "^7.4.3",
"@capacitor/core": "^7.4.3",
"@capacitor/filesystem": "7.1.4",
"@capacitor/keyboard": "^7.0.3",
+ "@capacitor/preferences": "^7.0.2",
"@capacitor/screen-orientation": "^7.0.2",
"@capacitor/status-bar": "^7.0.3",
"@capawesome/capacitor-file-picker": "^7.2.0",
@@ -26539,7 +26307,6 @@
"@langchain/community": "^0.3.49",
"@langchain/core": "^0.3.66",
"@langchain/openai": "^0.6.3",
- "@pulse-editor/capacitor-plugin": "file:../capacitor-plugin",
"@pulse-editor/shared-utils": "^0.1.1-beta.55",
"@ricky0123/vad-web": "^0.0.28",
"@vercel/analytics": "^1.5.0",
diff --git a/package.json b/package.json
index caaa7494..7195c3d1 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,6 @@
"workspaces": [
"web",
"mobile",
- "capacitor-plugin",
"vscode-extension",
"npm-packages/react-api",
"npm-packages/shared-utils"
@@ -17,12 +16,11 @@
"desktop-dev": "npm --prefix ./desktop run dev-https",
"desktop-build": "npm run web-build && npm --prefix ./desktop run build",
"android-dev": "npm run web-build && npm run android-dev --workspace=mobile",
- "android-build": "npm run web-build && npm run android-build --workspace=mobile",
+ "android-run": "npm run web-build && npm run android-run --workspace=mobile",
"react-api-build": "npm run build --workspace=npm-packages/react-api",
"shared-utils-build": "npm run build --workspace=npm-packages/shared-utils",
"cli-dev": "npm run dev --workspace=cli",
- "cli-build": "npm run build --workspace=cli",
- "capacitor-plugin-build": "npm run build --workspace=capacitor-plugin"
+ "cli-build": "npm run build --workspace=cli"
},
"devDependencies": {
"@changesets/cli": "^2.29.4",
@@ -30,4 +28,4 @@
"prettier-plugin-organize-imports": "^4.3.0",
"prettier-plugin-tailwindcss": "^0.6.14"
}
-}
+}
\ No newline at end of file
diff --git a/remote-instance/.env.example b/remote-instance/.env.example
deleted file mode 100644
index a29b64f2..00000000
--- a/remote-instance/.env.example
+++ /dev/null
@@ -1,4 +0,0 @@
-SSL_CERT_PATH=path_to_cert
-SSL_KEY_PATH=path_to_key
-# You can modify this to custom frontend deployment URL
-FRONTEND_URL=https://editor.pulse-editor.com
diff --git a/remote-instance/src/index.ts b/remote-instance/src/index.ts
deleted file mode 100644
index e4cce2ea..00000000
--- a/remote-instance/src/index.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { createTerminalServer } from "./servers/node-pty";
-import { createAPIServer } from "./servers/api-server";
-
-/* Create servers */
-createAPIServer().then((server) => {
- // After API server is created, the terminal server can use it
- createTerminalServer(server);
-});
diff --git a/remote-instance/src/servers/api-server/index.ts b/remote-instance/src/servers/api-server/index.ts
deleted file mode 100644
index 6b03be52..00000000
--- a/remote-instance/src/servers/api-server/index.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import express from "express";
-import https from "https";
-import http from "http";
-import fs from "fs";
-import dotenv from "dotenv";
-import { handlePlatformAPIRequest } from "./platform-api/handler";
-
-dotenv.config();
-
-const app = express();
-const HOST = "0.0.0.0";
-const HTTP_SERVER_PORT = 6080;
-const HTTPS_SERVER_PORT = 6443;
-const certPath = process.env.SSL_CERT_PATH;
-const keyPath = process.env.SSL_KEY_PATH;
-
-export async function createAPIServer() {
- await createEndpoints(app);
-
- if (
- certPath &&
- keyPath &&
- fs.existsSync(certPath) &&
- fs.existsSync(keyPath)
- ) {
- const server = https.createServer(
- {
- key: fs.readFileSync(keyPath),
- cert: fs.readFileSync(certPath),
- },
- app
- );
- server.listen(HTTPS_SERVER_PORT, HOST, () => {
- console.log(`HTTPS server is running on port ${HTTPS_SERVER_PORT}`);
- });
- return server;
- } else {
- const server = http.createServer(app);
- server.listen(HTTP_SERVER_PORT, HOST, () => {
- console.log(`HTTP server is running on port ${HTTP_SERVER_PORT}`);
- });
- return server;
- }
-}
-
-async function createEndpoints(app: express.Express) {
- app.use(express.json());
-
- app.get("/:instanceId/", (req, res) => {
- const instanceId = req.params.instanceId;
- if (instanceId !== process.env.INSTANCE_ID) {
- return res.status(400).send("Invalid instance ID");
- }
- // Get the requested URL
- const serverUrl = req.protocol + "://" + req.get("host") + req.originalUrl;
-
- // Redirect to https://editor.pulse-editor.com and append
- // this instance's URL as a query parameter
- const url = new URL(
- process.env.FRONTEND_URL ?? "https://editor.pulse-editor.com"
- );
- url.searchParams.append("instance", serverUrl);
- res.redirect(url.toString());
- });
-
- app.get("/:instanceId/test", (req, res) => {
- const instanceId = req.params.instanceId;
- if (instanceId !== process.env.INSTANCE_ID) {
- return res.status(400).send("Invalid instance ID");
- }
- res.send("Remote instance is running!");
- });
-
- app.post("/:instanceId/platform-api", async (req, res) => {
- const instanceId = req.params.instanceId;
- if (instanceId !== process.env.INSTANCE_ID) {
- return res.status(400).send("Invalid instance ID");
- }
-
- // Get json body
- const body = req.body;
-
- console.log("Received platform API request:", body);
-
- const host = req.host;
-
- const result = await handlePlatformAPIRequest(
- body,
- host,
- instanceId
- );
-
- // Process the request and send a response
- if (result && result.error) {
- res.status(400).json(result);
- } else {
- res.send(result);
- }
- });
-}
diff --git a/remote-instance/src/servers/api-server/platform-api/handler.ts b/remote-instance/src/servers/api-server/platform-api/handler.ts
deleted file mode 100644
index 4aae2ea1..00000000
--- a/remote-instance/src/servers/api-server/platform-api/handler.ts
+++ /dev/null
@@ -1,232 +0,0 @@
-import fs from "fs";
-import ignore from "ignore";
-import path from "path";
-
-// Define a safe root directory for projects. Can be overridden by env or configured as needed.
-const PROJECTS_ROOT = process.env.PROJECTS_ROOT ?? "/srv/projects";
-
-// Utility to resolve and validate user-supplied uri inside PROJECTS_ROOT
-function getSafePath(uri: string): string {
- // Prevent empty/undefined input
- if (!uri || typeof uri !== 'string') {
- throw new Error("Invalid project path");
- }
- // Resolve against the root directory
- const resolved = path.resolve(PROJECTS_ROOT, uri);
- // Use fs.realpathSync to follow symlinks
- let normalized;
- try {
- normalized = fs.realpathSync(resolved);
- } catch {
- // If path does not exist yet (e.g., on creation), just use resolved.
- normalized = resolved;
- }
- // Ensure the normalized path is inside the root
- if (!normalized.startsWith(PROJECTS_ROOT)) {
- throw new Error("Access to paths outside projects root denied");
- }
- return normalized;
-}
-// List all folders in a path
-async function handleListProjects(uri: string) {
- const rootPath = getSafePath(uri);
- const files = await fs.promises.readdir(rootPath, { withFileTypes: true });
- const folders = files
- .filter((file) => file.isDirectory())
- .map((file) => file.name)
- .map((projectName) => ({
- name: projectName,
- ctime: fs.statSync(path.join(rootPath, projectName)).ctime,
- }));
-
- return folders;
-}
-
-async function listPathContent(
- uri: string,
- options: any,
- baseUri: string | undefined = undefined
-) {
- const rootPath = getSafePath(uri);
- const files = await fs.promises.readdir(rootPath, { withFileTypes: true });
-
- const promise: Promise[] = files
- // Filter by file type
- .filter(
- (file) =>
- (options?.include === "folders" && file.isDirectory()) ||
- (options?.include === "files" && file.isFile()) ||
- options?.include === "all"
- )
- // Filter by gitignore
- .filter((file) => {
- if (!options?.gitignore) {
- return true;
- }
- const ig = ignore().add(options.gitignore);
-
- const filePath = baseUri
- ? path.relative(baseUri, path.join(uri, file.name))
- : file.name;
-
- const isIgnored = ig.ignores(filePath);
-
- return !isIgnored;
- })
- .map(async (file) => {
- const name = file.name;
- const absoluteUri = path.join(rootPath, name);
- if (file.isDirectory()) {
- return {
- name: name,
- isFolder: true,
- subDirItems: options.isRecursive
- ? await listPathContent(absoluteUri, options, baseUri ?? uri)
- : [],
- uri: absoluteUri.replace(/\\/g, "/"),
- };
- }
-
- return {
- name,
- isFolder: false,
- uri: absoluteUri.replace(/\\/g, "/"),
- };
- });
-
- return Promise.all(promise);
-}
-
-// Discover the content of a project
-async function handleListPathContent(uri: string, options: any) {
- return await listPathContent(uri, options);
-}
-
-async function handleCreateProject(uri: string) {
- // Create a folder at the validated path
- await fs.promises.mkdir(getSafePath(uri));
-}
-
-async function handleCreateFolder(uri: string) {
- // Create a folder at the validated path
- await fs.promises.mkdir(getSafePath(uri));
-}
-
-async function handleCreateFile(uri: string) {
- // Create a file at the validated path
- await fs.promises.writeFile(getSafePath(uri), "");
-}
-
-async function handleRename(oldUri: string, newUri: string) {
- await fs.promises.rename(getSafePath(oldUri), getSafePath(newUri));
-}
-
-async function handleDelete(uri: string) {
- await fs.promises.rm(getSafePath(uri), { recursive: true, force: true });
-}
-
-async function handleHasPath(uri: string) {
- return fs.existsSync(getSafePath(uri));
-}
-
-async function handleReadFile(uri: string) {
- // Read the file at validated path
- const data = await fs.promises.readFile(getSafePath(uri), "utf-8");
-
- return data;
-}
-
-async function handleWriteFile(data: any, uri: string) {
- // Write the data at validated path
- const safePath = getSafePath(uri);
- // create parent directory if it doesn't exist
- const dir = path.dirname(safePath);
- if (!fs.existsSync(dir)) {
- fs.mkdirSync(dir, { recursive: true });
- }
-
- fs.writeFileSync(safePath, data);
-}
-
-async function handleCopyFiles(from: string, to: string) {
- // Copy the files from the validated from path to the validated to path
- await fs.promises.cp(getSafePath(from), getSafePath(to), { recursive: true });
-}
-
-async function handleLoadSettings() {
- if (fs.existsSync(settingsPath)) {
- const data = fs.readFileSync(settingsPath, "utf-8");
- return JSON.parse(data);
- }
- return {};
-}
-
-async function handleSaveSettings(settings: any) {
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
-}
-
-async function handleGetInstallationPath() {
- const uri = "~/pulse-editor";
- return uri;
-}
-
-export async function handlePlatformAPIRequest(
- data: any,
- host: string,
- instanceId: string
-): Promise {
- const { operation, args } = data;
-
- if (operation === "select-dir") {
- throw new Error("Method not implemented.");
- } else if (operation === "select-file") {
- throw new Error("Method not implemented.");
- } else if (operation === "list-projects") {
- const { uri }: { uri: string } = args;
- return await handleListProjects(uri);
- } else if (operation === "list-path-content") {
- const { uri, options }: { uri: string; options?: any } = args;
-
- return await handleListPathContent(uri, options);
- } else if (operation === "create-project") {
- const { uri }: { uri: string } = args;
- await handleCreateProject(uri);
- } else if (operation === "create-folder") {
- const { uri }: { uri: string } = args;
- await handleCreateFolder(uri);
- } else if (operation === "create-file") {
- const { uri }: { uri: string } = args;
- await handleCreateFile(uri);
- } else if (operation === "rename") {
- const { oldUri, newUri }: { oldUri: string; newUri: string } = args;
- await handleRename(oldUri, newUri);
- } else if (operation === "delete") {
- const { uri }: { uri: string } = args;
- await handleDelete(uri);
- } else if (operation === "has-path") {
- const { uri }: { uri: string } = args;
- return await handleHasPath(uri);
- } else if (operation === "read-file") {
- const { uri }: { uri: string } = args;
- return handleReadFile(uri);
- } else if (operation === "write-file") {
- const { data, uri }: { data: any; uri: string } = args;
- await handleWriteFile(data, uri);
- } else if (operation === "copy-files") {
- const { from, to }: { from: string; to: string } = args;
- await handleCopyFiles(from, to);
- } else if (operation === "get-persistent-settings") {
- return handleLoadSettings();
- } else if (operation === "set-persistent-settings") {
- const { settings }: { settings: any } = args;
- await handleSaveSettings(settings);
- } else if (operation === "reset-persistent-settings") {
- await handleSaveSettings({});
- } else if (operation === "get-installation-path") {
- return await handleGetInstallationPath();
- } else if (operation === "create-terminal") {
- return `${host}/${instanceId}/terminal/ws`;
- }
- // Do not reflect input data back to the client, return an explicit error message.
- return { error: "Unknown operation" };
-}
diff --git a/remote-instance/.dockerignore b/remote-workspace/.dockerignore
similarity index 100%
rename from remote-instance/.dockerignore
rename to remote-workspace/.dockerignore
diff --git a/remote-workspace/.env.example b/remote-workspace/.env.example
new file mode 100644
index 00000000..5c00bfd2
--- /dev/null
+++ b/remote-workspace/.env.example
@@ -0,0 +1,9 @@
+SSL_CERT_PATH=path_to_cert
+SSL_KEY_PATH=path_to_key
+# You can modify this to custom frontend deployment URL
+FRONTEND_URL=https://web.pulse-editor.com
+# Name of the workspace instance
+WORKSPACE_ID=test-workspace
+# Port for the server to listen on.
+# Default to 6080
+SERVER_PORT=6080
diff --git a/remote-instance/.gitignore b/remote-workspace/.gitignore
similarity index 100%
rename from remote-instance/.gitignore
rename to remote-workspace/.gitignore
diff --git a/remote-instance/README.md b/remote-workspace/README.md
similarity index 100%
rename from remote-instance/README.md
rename to remote-workspace/README.md
diff --git a/remote-instance/dockerfile b/remote-workspace/dockerfile
similarity index 74%
rename from remote-instance/dockerfile
rename to remote-workspace/dockerfile
index ecd9727b..d76a87d3 100644
--- a/remote-instance/dockerfile
+++ b/remote-workspace/dockerfile
@@ -1,7 +1,7 @@
FROM ubuntu:latest
ENV NODE_VERSION=20
-WORKDIR /app
+WORKDIR /pulse-editor/server
# install curl
RUN apt update && apt install -y curl make python3 build-essential
@@ -12,11 +12,17 @@ RUN curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | b
# set env
ENV NVM_DIR=/root/.nvm
-COPY . /app
+COPY . /pulse-editor/server
# install node
RUN bash -c "source $NVM_DIR/nvm.sh && nvm install $NODE_VERSION && npm install && npm run build"
+# install pulse cli
+RUN bash -c "source $NVM_DIR/nvm.sh && npm install -g @pulse-editor/cli@beta"
+
+# install git, zip, unzip
+RUN apt install -y git zip unzip
+
# Generate self-signed certificate
# RUN bash -c "./utils/generate-self-signed.sh"
diff --git a/remote-instance/package-lock.json b/remote-workspace/package-lock.json
similarity index 98%
rename from remote-instance/package-lock.json
rename to remote-workspace/package-lock.json
index 002ef76a..17b57ef4 100644
--- a/remote-instance/package-lock.json
+++ b/remote-workspace/package-lock.json
@@ -1,19 +1,18 @@
{
- "name": "pulse-editor-remote-instance",
+ "name": "pulse-editor-remote-workspace",
"version": "1.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "pulse-editor-remote-instance",
+ "name": "pulse-editor-remote-workspace",
"version": "1.0.0",
"license": "MIT",
"dependencies": {
- "@pulse-editor/shared-utils": "^0.1.1-alpha.24",
"dotenv": "^17.2.0",
"express": "^5.1.0",
"ignore": "^7.0.5",
- "node-pty": "^1.1.0-beta34",
+ "node-pty": "^1.1.0-beta37",
"ws": "^8.18.3"
},
"devDependencies": {
@@ -466,11 +465,6 @@
"node": ">=18"
}
},
- "node_modules/@pulse-editor/shared-utils": {
- "version": "0.1.1-alpha.24",
- "resolved": "https://registry.npmjs.org/@pulse-editor/shared-utils/-/shared-utils-0.1.1-alpha.24.tgz",
- "integrity": "sha512-aHSrc1Ntpvvs8npeSa543v0imjd/20UdXiJtLS5g2CsJAWAayzMjEU/nL6aWaE2rn6xThq8xgfy2Iu1Wr/SclQ=="
- },
"node_modules/@types/body-parser": {
"version": "1.19.6",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz",
@@ -1179,9 +1173,9 @@
"license": "MIT"
},
"node_modules/node-pty": {
- "version": "1.1.0-beta34",
- "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta34.tgz",
- "integrity": "sha512-RraDtX9RS1G1I5iO7e4YIOIA4arzd4ZVCD4mZr7+szaNupoTg9fxDCRr0EanqS0Qlzgm3PIdHNbPmblJguJuyg==",
+ "version": "1.1.0-beta37",
+ "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta37.tgz",
+ "integrity": "sha512-Ys8AW98Atyu9cLV5QLQshvSTF+YMDksVi2ULkYNPAKLGzaDQbXuOEjStg4ZZuL4c8saOTh1+2PNlCoyuagBr1Q==",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {
diff --git a/remote-instance/package.json b/remote-workspace/package.json
similarity index 72%
rename from remote-instance/package.json
rename to remote-workspace/package.json
index 2ddffcd7..862881d0 100644
--- a/remote-instance/package.json
+++ b/remote-workspace/package.json
@@ -1,7 +1,7 @@
{
- "name": "pulse-editor-remote-instance",
+ "name": "pulse-editor-remote-workspace",
"version": "1.0.0",
- "description": "Remote instance backend for Pulse Editor",
+ "description": "Remote workspace backend for Pulse Editor",
"license": "MIT",
"author": "ClayPulse",
"main": "src/index.ts",
@@ -11,11 +11,10 @@
"start": "node dist/index.js"
},
"dependencies": {
- "@pulse-editor/shared-utils": "^0.1.1-alpha.24",
"dotenv": "^17.2.0",
"express": "^5.1.0",
"ignore": "^7.0.5",
- "node-pty": "^1.1.0-beta34",
+ "node-pty": "^1.1.0-beta37",
"ws": "^8.18.3"
},
"devDependencies": {
diff --git a/remote-workspace/src/index.ts b/remote-workspace/src/index.ts
new file mode 100644
index 00000000..cdb54667
--- /dev/null
+++ b/remote-workspace/src/index.ts
@@ -0,0 +1,68 @@
+import dotenv from "dotenv";
+import express from "express";
+import fs from "fs";
+import http from "http";
+import https from "https";
+import { getLocalNetworkIP } from "./lib/get-network";
+import { addAPIServer } from "./servers/api-server";
+import { addTerminalServer } from "./servers/node-pty";
+
+dotenv.config();
+
+const expressApp = express();
+const serverPort = process.env.SERVER_PORT
+ ? parseInt(process.env.SERVER_PORT)
+ : 6080;
+const certPath = process.env.SSL_CERT_PATH;
+const keyPath = process.env.SSL_KEY_PATH;
+const workspaceId = process.env.WORKSPACE_ID;
+const frontendUrl = process.env.FRONTEND_URL ?? "https://web.pulse-editor.com";
+
+async function startServers() {
+ if (!workspaceId) {
+ console.error("WORKSPACE_ID is not set in environment variables.");
+ process.exit(1);
+ }
+
+ /* Create servers */
+ const server = await createServer();
+ const address = getLocalNetworkIP();
+
+ const isHttps =
+ certPath && keyPath && fs.existsSync(certPath) && fs.existsSync(keyPath)
+ ? true
+ : false;
+
+ await addAPIServer(server, expressApp, workspaceId, serverPort, frontendUrl);
+ console.log(
+ `API server is running at ${isHttps ? "https" : "http"}://${address}:${serverPort}/${workspaceId}`,
+ );
+
+ await addTerminalServer(server, workspaceId);
+ console.log(
+ `Terminal server is running at ${isHttps ? "wss" : "ws"}://${address}:${serverPort}/${workspaceId}/terminal/ws`,
+ );
+}
+
+async function createServer() {
+ if (
+ certPath &&
+ keyPath &&
+ fs.existsSync(certPath) &&
+ fs.existsSync(keyPath)
+ ) {
+ const server = https.createServer(
+ {
+ key: fs.readFileSync(keyPath),
+ cert: fs.readFileSync(certPath),
+ },
+ expressApp,
+ );
+ return server;
+ } else {
+ const server = http.createServer(expressApp);
+ return server;
+ }
+}
+
+startServers();
diff --git a/remote-workspace/src/lib/get-network.ts b/remote-workspace/src/lib/get-network.ts
new file mode 100644
index 00000000..a8a46f05
--- /dev/null
+++ b/remote-workspace/src/lib/get-network.ts
@@ -0,0 +1,14 @@
+import { networkInterfaces } from "os";
+
+export function getLocalNetworkIP() {
+ const interfaces = networkInterfaces();
+ for (const iface of Object.values(interfaces)) {
+ if (!iface) continue;
+ for (const config of iface) {
+ if (config.family === "IPv4" && !config.internal) {
+ return config.address; // Returns the first non-internal IPv4 address
+ }
+ }
+ }
+ return "localhost"; // Fallback
+}
\ No newline at end of file
diff --git a/remote-workspace/src/servers/api-server/index.ts b/remote-workspace/src/servers/api-server/index.ts
new file mode 100644
index 00000000..f39e7bb9
--- /dev/null
+++ b/remote-workspace/src/servers/api-server/index.ts
@@ -0,0 +1,78 @@
+import dotenv from "dotenv";
+import express from "express";
+import http from "http";
+import https from "https";
+import { handlePlatformAPIRequest } from "./platform-api/handler";
+
+dotenv.config();
+
+const HOST = "0.0.0.0";
+
+export async function addAPIServer(
+ server: http.Server | https.Server,
+ expressApp: express.Express,
+ instanceId: string,
+ port: number,
+ frontendUrl: string,
+) {
+ await createEndpoints(expressApp, instanceId, frontendUrl);
+
+ server.listen(port, HOST);
+}
+
+async function createEndpoints(
+ app: express.Express,
+ instanceId: string,
+ frontendUrl: string,
+) {
+ app.use(express.json());
+
+ app.get("/:instanceId/", (req, res) => {
+ const id = req.params.instanceId;
+ if (id !== instanceId) {
+ return res.status(400).send("Invalid instance ID");
+ }
+ // Get the requested URL
+ const serverUrl = req.protocol + "://" + req.get("host") + req.originalUrl;
+
+ // Redirect to https://editor.pulse-editor.com and append
+ // this instance's URL as a query parameter
+ const url = new URL(frontendUrl);
+ url.searchParams.append("instance", serverUrl);
+ res.redirect(url.toString());
+ });
+
+ app.get("/:instanceId/test", (req, res) => {
+ const id = req.params.instanceId;
+ if (id !== instanceId) {
+ return res.status(400).send("Invalid instance ID");
+ }
+ res.send("Remote instance is running!");
+ });
+
+ app.post("/:instanceId/platform-api", async (req, res) => {
+ const id = req.params.instanceId;
+ if (id !== instanceId) {
+ return res.status(400).send("Invalid instance ID");
+ }
+
+ // Get json body
+ const { operation, args } = req.body;
+
+ const result = await handlePlatformAPIRequest(
+ {
+ operation,
+ args,
+ },
+ req.get("host") ?? "",
+ instanceId,
+ );
+
+ // Process the request and send a response
+ if (result && result.error) {
+ res.status(400).json(result);
+ } else {
+ res.send(JSON.stringify(result));
+ }
+ });
+}
diff --git a/remote-workspace/src/servers/api-server/platform-api/handler.ts b/remote-workspace/src/servers/api-server/platform-api/handler.ts
new file mode 100644
index 00000000..e8cdea24
--- /dev/null
+++ b/remote-workspace/src/servers/api-server/platform-api/handler.ts
@@ -0,0 +1,286 @@
+import fs from "fs";
+import ignore from "ignore";
+import path from "path";
+
+// Define a safe root directory for projects. Can be overridden by env or configured as needed.
+
+const settingsPath = path.join("/pulse-editor", "settings.json");
+
+export async function handlePlatformAPIRequest(
+ data: {
+ operation: string;
+ args: any;
+ },
+ host: string,
+ instanceId: string,
+): Promise {
+ const { operation, args } = data;
+
+ switch (operation) {
+ case "select-dir":
+ // Folder picker is done via web interface
+ throw new Error("Method not implemented.");
+ case "select-file":
+ // File picker is done via web interface
+ throw new Error("Method not implemented.");
+ case "list-projects": {
+ const { uri }: { uri: string } = args;
+ return await handleListProjects(uri);
+ }
+ case "list-path-content": {
+ const { uri, options }: { uri: string; options?: any } = args;
+ return await handleListPathContent(uri, options);
+ }
+ case "create-project": {
+ const { uri }: { uri: string } = args;
+ await handleCreateProject(uri);
+ return;
+ }
+ case "delete-project": {
+ const { uri }: { uri: string } = args;
+ await handleDeleteProject(uri);
+ return;
+ }
+ case "update-project": {
+ const {
+ uri,
+ updatedInfo,
+ }: {
+ uri: string;
+ updatedInfo: {
+ name: string;
+ ctime?: Date;
+ };
+ } = args;
+ await handleUpdateProject(uri, updatedInfo);
+ return;
+ }
+ case "create-folder": {
+ const { uri }: { uri: string } = args;
+ await handleCreateFolder(uri);
+ return;
+ }
+ case "create-file": {
+ const { uri }: { uri: string } = args;
+ await handleCreateFile(uri);
+ return;
+ }
+ case "rename": {
+ const { oldUri, newUri }: { oldUri: string; newUri: string } = args;
+ await handleRename(oldUri, newUri);
+ return;
+ }
+ case "delete": {
+ const { uri }: { uri: string } = args;
+ await handleDelete(uri);
+ return;
+ }
+ case "has-path": {
+ const { uri }: { uri: string } = args;
+ return await handleHasPath(uri);
+ }
+ case "read-file": {
+ const { uri }: { uri: string } = args;
+ return await handleReadFile(uri);
+ }
+ case "write-file": {
+ const { data, uri }: { data: any; uri: string } = args;
+ await handleWriteFile(data, uri);
+ return;
+ }
+ case "copy-files": {
+ const { from, to }: { from: string; to: string } = args;
+ await handleCopyFiles(from, to);
+ return;
+ }
+ case "get-persistent-settings":
+ return handleLoadSettings();
+ case "set-persistent-settings": {
+ const { settings }: { settings: any } = args;
+ await handleSaveSettings(settings);
+ return;
+ }
+ case "get-installation-path":
+ return await handleGetInstallationPath();
+ case "create-terminal":
+ return `${host}/${instanceId}/terminal/ws`;
+ default:
+ // Do not reflect input data back to the client, return an explicit error message.
+ return { error: "Unknown operation" };
+ }
+}
+
+
+// List all folders in a path
+async function handleListProjects(uri: string) {
+ const rootPath = uri;
+ const files = await fs.promises.readdir(rootPath, { withFileTypes: true });
+ const folders = files
+ .filter((file) => file.isDirectory())
+ .map((file) => file.name)
+ .map((projectName) => ({
+ name: projectName,
+ ctime: fs.statSync(path.join(rootPath, projectName)).ctime,
+ }));
+
+ return folders;
+}
+
+async function listPathContent(
+ uri: string,
+ options: any,
+ baseUri: string | undefined = undefined,
+) {
+ const rootPath = uri;
+ const files = await fs.promises.readdir(rootPath, { withFileTypes: true });
+
+ const promise: Promise[] = files
+ // Filter by file type
+ .filter(
+ (file) =>
+ (options?.include === "folders" && file.isDirectory()) ||
+ (options?.include === "files" && file.isFile()) ||
+ options?.include === "all",
+ )
+ // Filter by gitignore
+ .filter((file) => {
+ if (!options?.gitignore) {
+ return true;
+ }
+ const ig = ignore().add(options.gitignore);
+
+ const filePath = baseUri
+ ? path.relative(baseUri, path.join(uri, file.name))
+ : file.name;
+
+ const isIgnored = ig.ignores(filePath);
+
+ return !isIgnored;
+ })
+ .map(async (file) => {
+ const name = file.name;
+ const absoluteUri = path.join(rootPath, name);
+ if (file.isDirectory()) {
+ return {
+ name: name,
+ isFolder: true,
+ subDirItems: options.isRecursive
+ ? await listPathContent(absoluteUri, options, baseUri ?? uri)
+ : [],
+ uri: absoluteUri.replace(/\\/g, "/"),
+ };
+ }
+
+ return {
+ name,
+ isFolder: false,
+ uri: absoluteUri.replace(/\\/g, "/"),
+ };
+ });
+
+ return Promise.all(promise);
+}
+
+// Discover the content of a project
+async function handleListPathContent(uri: string, options: any) {
+ return await listPathContent(uri, options);
+}
+
+async function handleCreateProject(uri: string) {
+ // Create a folder at the validated path
+ await fs.promises.mkdir(uri);
+}
+
+async function handleDeleteProject(uri: string) {
+ // Delete the folder at the validated path
+ await fs.promises.rm(uri, { recursive: true, force: true });
+}
+
+async function handleUpdateProject(
+ uri: string,
+ updatedInfo: {
+ name: string;
+ ctime?: Date;
+ },
+) {
+ const newUri = path.join(path.dirname(uri), updatedInfo.name);
+ await fs.promises.rename(uri, newUri);
+}
+
+async function handleCreateFolder(uri: string) {
+ // Create a folder at the validated path
+ await fs.promises.mkdir(uri);
+}
+
+async function handleCreateFile(uri: string) {
+ // Create a file at the validated path
+ await fs.promises.writeFile(uri, "");
+}
+
+async function handleRename(oldUri: string, newUri: string) {
+ await fs.promises.rename(
+ oldUri,
+ newUri,
+ );
+}
+
+async function handleDelete(uri: string) {
+ await fs.promises.rm(uri, {
+ recursive: true,
+ force: true,
+ });
+}
+
+async function handleHasPath(uri: string) {
+ return fs.existsSync(uri);
+}
+
+async function handleReadFile(uri: string) {
+ // Read the file at validated path
+ const data = await fs.promises.readFile(
+ uri,
+ "utf-8",
+ );
+
+ return data;
+}
+
+async function handleWriteFile(data: any, uri: string) {
+ // Write the data at validated path
+ const safePath = uri;
+ // create parent directory if it doesn't exist
+ const dir = path.dirname(safePath);
+ if (!fs.existsSync(dir)) {
+ fs.mkdirSync(dir, { recursive: true });
+ }
+
+ fs.writeFileSync(safePath, data);
+}
+
+async function handleCopyFiles(from: string, to: string) {
+ // Copy the files from the validated from path to the validated to path
+ await fs.promises.cp(
+ from,
+ to,
+ {
+ recursive: true,
+ },
+ );
+}
+
+async function handleLoadSettings() {
+ if (fs.existsSync(settingsPath)) {
+ const data = fs.readFileSync(settingsPath, "utf-8");
+ return JSON.parse(data);
+ }
+ return {};
+}
+
+async function handleSaveSettings(settings: any) {
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
+}
+
+async function handleGetInstallationPath() {
+ const uri = "~/pulse-editor";
+ return uri;
+}
diff --git a/remote-instance/src/servers/node-pty/index.ts b/remote-workspace/src/servers/node-pty/index.ts
similarity index 72%
rename from remote-instance/src/servers/node-pty/index.ts
rename to remote-workspace/src/servers/node-pty/index.ts
index 807fcd6e..6810742d 100644
--- a/remote-instance/src/servers/node-pty/index.ts
+++ b/remote-workspace/src/servers/node-pty/index.ts
@@ -13,6 +13,7 @@ const spawnShell = () => {
return spawn(shell, [], {
name: "xterm-color",
env: process.env,
+ cwd: "/workspace"
});
};
@@ -26,14 +27,20 @@ const setSharedTerminalMode = (useSharedTerminal: boolean) => {
const handleTerminalConnection = (ws: WebSocket) => {
let ptyProcess = sharedTerminalMode ? sharedPtyProcess : spawnShell();
- ws.on("message", (command: string) => {
- const processedCommand = commandProcessor(command);
- ptyProcess?.write(processedCommand);
+ ws.on("message", (data: string) => {
+ const dataObj = JSON.parse(data);
+
+ if (dataObj.type === "input") {
+ const command = dataObj.payload;
+ ptyProcess?.write(command);
+ } else if (dataObj.type === "resize") {
+ const { cols, rows } = dataObj.payload;
+ ptyProcess?.resize(cols, rows);
+ }
});
ptyProcess?.onData((rawOutput) => {
- const processedOutput = outputProcessor(rawOutput);
- ws.send(processedOutput);
+ ws.send(JSON.stringify({ type: "output", payload: rawOutput }));
});
ws.on("close", () => {
@@ -43,20 +50,14 @@ const handleTerminalConnection = (ws: WebSocket) => {
});
};
-// Utility function to process commands
-const commandProcessor = (command: string) => {
- return command;
-};
-
-// Utility function to process output
-const outputProcessor = (output: string) => {
- return output;
-};
/* Host ws node-pty server */
-setSharedTerminalMode(false); // Set this to false to allow a shared session
+setSharedTerminalMode(true);
-export function createTerminalServer(server: http.Server | https.Server) {
+export function addTerminalServer(
+ server: http.Server | https.Server,
+ instanceId: string,
+) {
const wss = new WebSocketServer({ noServer: true });
wss.on("connection", handleTerminalConnection);
@@ -80,8 +81,8 @@ export function createTerminalServer(server: http.Server | https.Server) {
return;
}
- const instanceId = match?.[1];
- if (instanceId !== process.env.INSTANCE_ID) {
+ const id = match?.[1];
+ if (id !== instanceId) {
socket.write("HTTP/1.1 400 Bad Request: Invalid instance ID\r\n\r\n");
socket.destroy();
return;
diff --git a/remote-instance/tsconfig.json b/remote-workspace/tsconfig.json
similarity index 90%
rename from remote-instance/tsconfig.json
rename to remote-workspace/tsconfig.json
index fe93931e..cd690105 100644
--- a/remote-instance/tsconfig.json
+++ b/remote-workspace/tsconfig.json
@@ -1,7 +1,7 @@
{
"compilerOptions": {
"target": "ES2020",
- "module": "node20",
+ "module": "nodenext",
"moduleResolution": "nodenext",
"outDir": "dist",
"rootDir": "src",
diff --git a/remote-instance/utils/generate-self-signed.ps1 b/remote-workspace/utils/generate-self-signed.ps1
similarity index 100%
rename from remote-instance/utils/generate-self-signed.ps1
rename to remote-workspace/utils/generate-self-signed.ps1
diff --git a/remote-instance/utils/generate-self-signed.sh b/remote-workspace/utils/generate-self-signed.sh
similarity index 100%
rename from remote-instance/utils/generate-self-signed.sh
rename to remote-workspace/utils/generate-self-signed.sh
diff --git a/web/README.md b/web/README.md
index d788fa9b..ad78fd69 100644
--- a/web/README.md
+++ b/web/README.md
@@ -1,12 +1,66 @@
+# Generate certificates for local dev
+
+## Do the below in `web/certificates` folder
+
+1. Generate a local CA.
+ Use devCA.key to install on device later.
+
+```bash
+openssl genrsa -out devCA.key 2048
+openssl req -x509 -new -nodes -key devCA.key -sha256 -days 3650 -out devCA.crt -subj "/CN=Local Development CA"
+```
+
+2. Create a .cnf
+ e.g.
+
+```bash
+[req]
+default_bits = 2048
+prompt = no
+default_md = sha256
+distinguished_name = dn
+req_extensions = req_ext
+
+[dn]
+CN = 192.168.1.100
+
+[req_ext]
+subjectAltName = @alt_names
+
+[alt_names]
+IP.1 = 192.168.1.100
+DNS.1 = mypc
+DNS.2 = mypc.local
+DNS.3 = localhost
+```
+
+3. Generate private key
+
+```bash
+openssl genrsa -out localhost-key.pem 2048
+openssl req -new -key localhost-key.pem -out localhost.csr -config localhost.cnf
+```
+
+4. Sign the certificate with CA
+
+```bash
+openssl x509 -req -in localhost.csr -CA devCA.crt -CAkey devCA.key -CAcreateserial -out localhost.pem -days 365 -sha256 -extensions req_ext -extfile localhost.cnf
+```
+
+5. Install `devCA.crt` on Android.
+ First, copy `devCA.crt` from PC to you android device. On Android, go to "settings -> security and privacy -> more security settings -> install from device storage -> CA certificate", then locate `devCA.crt` and install.
+
# Web Client User Guide
+
### Installation
-You must then configure settings in the app. Specifically, to use Voice Chat, you need to have all STT, LLM, TTS configured; to only use Agentic Chat Terminal or Code Completion, you need to configure LLM.
+
+You must then configure settings in the app. Specifically, to use Voice Chat, you need to have all STT, LLM, TTS configured; to only use Agentic Chat Terminal or Code Completion, you need to configure LLM.
| Modality | Supported Provider |
-| --- | --- |
-| STT | OpenAI |
-| LLM | OpenAI |
-| TTS | OpenAI, ElevenLabs |
+| -------- | ------------------ |
+| STT | OpenAI |
+| LLM | OpenAI |
+| TTS | OpenAI, ElevenLabs |
(For TTS, you need to enter a voice name or voice ID which you can find from your provider. e.g. “alloy” for OpenAI TTS1, “Maltida” for ElevenLabs.)
@@ -34,4 +88,4 @@ Click the “Open Chat View” icon in the bottom toolbar. Then select your desi
(Make sure you have configured LLM provider and API key)
-Type anything in an open file, then a suggestion would become available in grey text. Press tab key to accept changes, or keep typing to refresh new suggest.
\ No newline at end of file
+Type anything in an open file, then a suggestion would become available in grey text. Press tab key to accept changes, or keep typing to refresh new suggest.
diff --git a/web/app/(main-layout)/layout.tsx b/web/app/(main-layout)/layout.tsx
index 48165913..f0c5e57f 100644
--- a/web/app/(main-layout)/layout.tsx
+++ b/web/app/(main-layout)/layout.tsx
@@ -1,16 +1,16 @@
-import type { Metadata } from "next";
-import "./globals.css";
-import WrappedHeroUIProvider from "@/components/providers/wrapped-hero-ui-provider";
-import EditorContextProvider from "@/components/providers/editor-context-provider";
-import { Toaster } from "react-hot-toast";
-import "material-icons/iconfont/material-icons.css";
+import Nav from "@/components/interface/navigation/nav";
import CapacitorProvider from "@/components/providers/capacitor-provider";
-import RemoteModuleProvider from "@/components/providers/remote-module-provider";
+import EditorContextProvider from "@/components/providers/editor-context-provider";
import InterModuleCommunicationProvider from "@/components/providers/imc-provider";
-import Nav from "@/components/interface/navigation/nav";
-import { Suspense } from "react";
-import { Analytics } from "@vercel/analytics/next";
import PlatformAssistantProvider from "@/components/providers/platform-assistant-provider";
+import RemoteModuleProvider from "@/components/providers/remote-module-provider";
+import WrappedHeroUIProvider from "@/components/providers/wrapped-hero-ui-provider";
+import { Analytics } from "@vercel/analytics/next";
+import "material-icons/iconfont/material-icons.css";
+import type { Metadata } from "next";
+import { Suspense } from "react";
+import { Toaster } from "react-hot-toast";
+import "./globals.css";
export const metadata: Metadata = {
title: "Pulse Editor",
@@ -27,9 +27,9 @@ export default function RootLayout({
-
-
-
+
+
+
@@ -40,9 +40,9 @@ export default function RootLayout({
-
-
-
+
+
+