Compare commits
93 Commits
master
...
firebase-a
| Author | SHA1 | Date | |
|---|---|---|---|
| e74b510d59 | |||
| f4a952c76f | |||
| fdb1543ffa | |||
| 69255d0734 | |||
| 73a1a61d23 | |||
| d793a4260c | |||
| 5e1ff59880 | |||
| 33737167b4 | |||
| c21538c09f | |||
| 1f3a79bc99 | |||
| d6c55da727 | |||
| 6e725ec966 | |||
|
|
ccb3c98fe8 | ||
|
|
fb07da9599 | ||
|
|
75a87378ee | ||
| b17ce32b4d | |||
| a837305f2a | |||
| bbc8010da3 | |||
| 99bc820a71 | |||
| af41c3533f | |||
| 5f5987bf5e | |||
| 2b15178184 | |||
| c3459f689a | |||
| 65465f79e6 | |||
| f1ddb2d736 | |||
| a5ef1c5333 | |||
| c21effa5fa | |||
| e3f11f2b35 | |||
| 2959fbd811 | |||
| 7708f25caa | |||
| 8e9af01147 | |||
| bcb956ae6e | |||
| 13d6364ffe | |||
| 37e677ec6a | |||
| 28f2f9a9ca | |||
| c76bf3f6d8 | |||
|
|
0f48494469 | ||
|
|
41390233c1 | ||
|
|
7c1e74ff39 | ||
|
|
3e6b9414f4 | ||
| 906121882b | |||
| f6e5d5ca4f | |||
| 5a4bba2dcd | |||
|
|
1ab3db6cce | ||
|
|
ee8afdf2a7 | ||
|
|
f059cc0291 | ||
| 4becbf41a9 | |||
| cf8d965e64 | |||
| 79f81cb83f | |||
| 117fff9b02 | |||
| 32bdacda88 | |||
| b2c1f7d11d | |||
| fc9d8d1023 | |||
| f82b28b896 | |||
|
|
3a40c95496 | ||
|
|
f102dd1ff0 | ||
| 6f19ee94d1 | |||
| 6a53d9fd58 | |||
| b7e5da2354 | |||
|
|
d45ceb9b41 | ||
|
|
24dc9482da | ||
| dd2a3d91ca | |||
| 4d4938a0ad | |||
| 021e0f3eb4 | |||
| 16c92ed33f | |||
| 87c3ff52ef | |||
| 704c955278 | |||
| e2386ef681 | |||
| ba0b6b5042 | |||
| 07eb24006d | |||
| 65b641866e | |||
| 16c93b2d10 | |||
| c3cfc11f5f | |||
| cf00a57a7d | |||
| f61be632a0 | |||
| 7e7bb41a27 | |||
| f9cbc88303 | |||
| 1f38d6a1ac | |||
| b0224826ec | |||
| aa02e66245 | |||
| b52ae59817 | |||
| 0ac2e00399 | |||
| 14018e7606 | |||
| e802ad289c | |||
|
|
35b016449a | ||
|
|
0619707054 | ||
| d45045d63f | |||
| 22017f3e8b | |||
| 6c52785597 | |||
| 9765806dca | |||
| 15147a547d | |||
| 54af4cdc7e | |||
|
|
4a47f6d0a4 |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/prettierrc",
|
"$schema": "https://json.schemastore.org/prettierrc",
|
||||||
"semi": false,
|
"semi": false,
|
||||||
"tabWidth": 4,
|
"tabWidth": 2,
|
||||||
"singleQuote": true,
|
"singleQuote": true,
|
||||||
"printWidth": 100,
|
"printWidth": 100,
|
||||||
"trailingComma": "none",
|
"trailingComma": "none",
|
||||||
|
|||||||
9
Makefile
@@ -1,11 +1,2 @@
|
|||||||
pretty:
|
pretty:
|
||||||
npx prettier --write "./src/**/*.(ts|vue)"
|
npx prettier --write "./src/**/*.(ts|vue)"
|
||||||
|
|
||||||
check:
|
|
||||||
npm run type-check && npm run lint
|
|
||||||
|
|
||||||
local: check
|
|
||||||
npm run local
|
|
||||||
|
|
||||||
deploy: check
|
|
||||||
npm run deploy
|
|
||||||
|
|||||||
@@ -13,7 +13,6 @@ dependencies {
|
|||||||
implementation project(':capacitor-dialog')
|
implementation project(':capacitor-dialog')
|
||||||
implementation project(':capacitor-haptics')
|
implementation project(':capacitor-haptics')
|
||||||
implementation project(':capacitor-keyboard')
|
implementation project(':capacitor-keyboard')
|
||||||
implementation project(':capacitor-splash-screen')
|
|
||||||
implementation project(':capacitor-status-bar')
|
implementation project(':capacitor-status-bar')
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 4.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.4 KiB |
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 9.6 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 6.6 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<color name="ic_launcher_background">#1E4BC4</color>
|
<color name="ic_launcher_background">#FFFFFF</color>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -14,8 +14,5 @@ project(':capacitor-haptics').projectDir = new File('../node_modules/@capacitor/
|
|||||||
include ':capacitor-keyboard'
|
include ':capacitor-keyboard'
|
||||||
project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android')
|
project(':capacitor-keyboard').projectDir = new File('../node_modules/@capacitor/keyboard/android')
|
||||||
|
|
||||||
include ':capacitor-splash-screen'
|
|
||||||
project(':capacitor-splash-screen').projectDir = new File('../node_modules/@capacitor/splash-screen/android')
|
|
||||||
|
|
||||||
include ':capacitor-status-bar'
|
include ':capacitor-status-bar'
|
||||||
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')
|
project(':capacitor-status-bar').projectDir = new File('../node_modules/@capacitor/status-bar/android')
|
||||||
|
|||||||
@@ -8,15 +8,12 @@ const config: CapacitorConfig = {
|
|||||||
androidScheme: 'https'
|
androidScheme: 'https'
|
||||||
},
|
},
|
||||||
plugins: {
|
plugins: {
|
||||||
SplashScreen: {
|
|
||||||
backgroundColor: '#1E4BC4'
|
|
||||||
},
|
|
||||||
Keyboard: {
|
Keyboard: {
|
||||||
resize: 'native'
|
resize: 'native'
|
||||||
},
|
},
|
||||||
FirebaseAuthentication: {
|
FirebaseAuthentication: {
|
||||||
skipNativeAuth: false,
|
skipNativeAuth: false,
|
||||||
providers: ['google.com', 'microsoft.com', 'github.com']
|
providers: ['google.com']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
|
504EC3121FED79650016851F /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 504EC3101FED79650016851F /* LaunchScreen.storyboard */; };
|
||||||
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
|
50B271D11FEDC1A000F3C39B /* public in Resources */ = {isa = PBXBuildFile; fileRef = 50B271D01FEDC1A000F3C39B /* public */; };
|
||||||
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
|
A084ECDBA7D38E1E42DFC39D /* Pods_App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */; };
|
||||||
A9A186E62A2FB826009CBA16 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = A9A186E52A2FB826009CBA16 /* GoogleService-Info.plist */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@@ -28,7 +27,6 @@
|
|||||||
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
504EC3111FED79650016851F /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
504EC3131FED79650016851F /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
|
50B271D01FEDC1A000F3C39B /* public */ = {isa = PBXFileReference; lastKnownFileType = folder; path = public; sourceTree = "<group>"; };
|
||||||
A9A186E52A2FB826009CBA16 /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = "<group>"; };
|
|
||||||
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
AF277DCFFFF123FFC6DF26C7 /* Pods_App.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_App.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
|
AF51FD2D460BCFE21FA515B2 /* Pods-App.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.release.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
|
FC68EB0AF532CFC21C3344DD /* Pods-App.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-App.debug.xcconfig"; path = "Pods/Target Support Files/Pods-App/Pods-App.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
@@ -76,7 +74,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
50379B222058CBB4000EE86E /* capacitor.config.json */,
|
50379B222058CBB4000EE86E /* capacitor.config.json */,
|
||||||
A9A186E52A2FB826009CBA16 /* GoogleService-Info.plist */,
|
|
||||||
504EC3071FED79650016851F /* AppDelegate.swift */,
|
504EC3071FED79650016851F /* AppDelegate.swift */,
|
||||||
504EC30B1FED79650016851F /* Main.storyboard */,
|
504EC30B1FED79650016851F /* Main.storyboard */,
|
||||||
504EC30E1FED79650016851F /* Assets.xcassets */,
|
504EC30E1FED79650016851F /* Assets.xcassets */,
|
||||||
@@ -164,7 +161,6 @@
|
|||||||
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
|
50379B232058CBB4000EE86E /* capacitor.config.json in Resources */,
|
||||||
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
|
504EC30D1FED79650016851F /* Main.storyboard in Resources */,
|
||||||
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
|
2FAD9763203C412B000D30F8 /* config.xml in Resources */,
|
||||||
A9A186E62A2FB826009CBA16 /* GoogleService-Info.plist in Resources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -351,7 +347,6 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = 5XQS3G6YV7;
|
|
||||||
INFOPLIST_FILE = App/Info.plist;
|
INFOPLIST_FILE = App/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
@@ -372,7 +367,6 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 1;
|
||||||
DEVELOPMENT_TEAM = 5XQS3G6YV7;
|
|
||||||
INFOPLIST_FILE = App/Info.plist;
|
INFOPLIST_FILE = App/Info.plist;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 13.0;
|
||||||
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 68 KiB |
|
After Width: | Height: | Size: 108 KiB |
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "AppIcon-1024@2x.png",
|
"filename" : "AppIcon-512@2x.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"platform" : "ios",
|
"platform" : "ios",
|
||||||
"size" : "1024x1024"
|
"size" : "1024x1024"
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
"filename" : "splash-landscape@1x.png",
|
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
|
"filename" : "splash-2732x2732-2.png",
|
||||||
"scale" : "1x"
|
"scale" : "1x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "splash-landscape@2x.png",
|
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
|
"filename" : "splash-2732x2732-1.png",
|
||||||
"scale" : "2x"
|
"scale" : "2x"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"filename" : "splash-landscape@3x.png",
|
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
|
"filename" : "splash-2732x2732.png",
|
||||||
"scale" : "3x"
|
"scale" : "3x"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"info" : {
|
"info" : {
|
||||||
"author" : "xcode",
|
"version" : 1,
|
||||||
"version" : 1
|
"author" : "xcode"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIN
ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-1.png
vendored
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732-2.png
vendored
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
ios/App/App/Assets.xcassets/Splash.imageset/splash-2732x2732.png
vendored
Normal file
|
After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 130 KiB |
|
Before Width: | Height: | Size: 130 KiB |
@@ -1,38 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>CLIENT_ID</key>
|
|
||||||
<string>1048273547256-fes4qqj6po81ki21doggnst5ptjsa940.apps.googleusercontent.com</string>
|
|
||||||
<key>REVERSED_CLIENT_ID</key>
|
|
||||||
<string>com.googleusercontent.apps.1048273547256-fes4qqj6po81ki21doggnst5ptjsa940</string>
|
|
||||||
<key>ANDROID_CLIENT_ID</key>
|
|
||||||
<string>1048273547256-qq9j4hcvni377p8cva7v10u9us1pjrc9.apps.googleusercontent.com</string>
|
|
||||||
<key>API_KEY</key>
|
|
||||||
<string>AIzaSyCkN_Fm0wGcDZJJ6ltz5NHDvs9zPgKiSTQ</string>
|
|
||||||
<key>GCM_SENDER_ID</key>
|
|
||||||
<string>1048273547256</string>
|
|
||||||
<key>PLIST_VERSION</key>
|
|
||||||
<string>1</string>
|
|
||||||
<key>BUNDLE_ID</key>
|
|
||||||
<string>com.contexted.app</string>
|
|
||||||
<key>PROJECT_ID</key>
|
|
||||||
<string>contexted-f8b4e</string>
|
|
||||||
<key>STORAGE_BUCKET</key>
|
|
||||||
<string>contexted-f8b4e.appspot.com</string>
|
|
||||||
<key>IS_ADS_ENABLED</key>
|
|
||||||
<false></false>
|
|
||||||
<key>IS_ANALYTICS_ENABLED</key>
|
|
||||||
<false></false>
|
|
||||||
<key>IS_APPINVITE_ENABLED</key>
|
|
||||||
<true></true>
|
|
||||||
<key>IS_GCM_ENABLED</key>
|
|
||||||
<true></true>
|
|
||||||
<key>IS_SIGNIN_ENABLED</key>
|
|
||||||
<true></true>
|
|
||||||
<key>GOOGLE_APP_ID</key>
|
|
||||||
<string>1:1048273547256:ios:e57d78bd483100900f700d</string>
|
|
||||||
<key>DATABASE_URL</key>
|
|
||||||
<string>https://contexted-f8b4e.firebaseio.com</string>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Contexted</string>
|
<string>Contexted</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@@ -18,19 +18,6 @@
|
|||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>$(MARKETING_VERSION)</string>
|
<string>$(MARKETING_VERSION)</string>
|
||||||
<key>CFBundleURLTypes</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>CFBundleTypeRole</key>
|
|
||||||
<string>Editor</string>
|
|
||||||
<key>CFBundleURLName</key>
|
|
||||||
<string>Firebase</string>
|
|
||||||
<key>CFBundleURLSchemes</key>
|
|
||||||
<array>
|
|
||||||
<string>com.googleusercontent.apps.1048273547256-fes4qqj6po81ki21doggnst5ptjsa940</string>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>$(CURRENT_PROJECT_VERSION)</string>
|
<string>$(CURRENT_PROJECT_VERSION)</string>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ def capacitor_pods
|
|||||||
pod 'CapacitorDialog', :path => '../../node_modules/@capacitor/dialog'
|
pod 'CapacitorDialog', :path => '../../node_modules/@capacitor/dialog'
|
||||||
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
|
pod 'CapacitorHaptics', :path => '../../node_modules/@capacitor/haptics'
|
||||||
pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
|
pod 'CapacitorKeyboard', :path => '../../node_modules/@capacitor/keyboard'
|
||||||
pod 'CapacitorSplashScreen', :path => '../../node_modules/@capacitor/splash-screen'
|
|
||||||
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
pod 'CapacitorStatusBar', :path => '../../node_modules/@capacitor/status-bar'
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -27,6 +26,9 @@ end
|
|||||||
|
|
||||||
post_install do |installer|
|
post_install do |installer|
|
||||||
assertDeploymentTarget(installer)
|
assertDeploymentTarget(installer)
|
||||||
|
end
|
||||||
|
|
||||||
|
post_install do |installer|
|
||||||
installer.pods_project.targets.each do |target|
|
installer.pods_project.targets.each do |target|
|
||||||
target.build_configurations.each do |config|
|
target.build_configurations.each do |config|
|
||||||
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
|
if target.respond_to?(:product_type) and target.product_type == "com.apple.product-type.bundle"
|
||||||
|
|||||||
@@ -1,96 +1,24 @@
|
|||||||
PODS:
|
PODS:
|
||||||
- AppAuth (1.6.2):
|
|
||||||
- AppAuth/Core (= 1.6.2)
|
|
||||||
- AppAuth/ExternalUserAgent (= 1.6.2)
|
|
||||||
- AppAuth/Core (1.6.2)
|
|
||||||
- AppAuth/ExternalUserAgent (1.6.2):
|
|
||||||
- AppAuth/Core
|
|
||||||
- Capacitor (5.0.4):
|
- Capacitor (5.0.4):
|
||||||
- CapacitorCordova
|
- CapacitorCordova
|
||||||
- CapacitorCordova (5.0.4)
|
- CapacitorCordova (5.0.4)
|
||||||
- CapacitorDialog (5.0.2):
|
- CapacitorDialog (5.0.2):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorFirebaseAuthentication (5.0.0):
|
|
||||||
- Capacitor
|
|
||||||
- CapacitorFirebaseAuthentication/Lite (= 5.0.0)
|
|
||||||
- FirebaseAuth (= 10.8.0)
|
|
||||||
- CapacitorFirebaseAuthentication/Google (5.0.0):
|
|
||||||
- Capacitor
|
|
||||||
- FirebaseAuth (= 10.8.0)
|
|
||||||
- GoogleSignIn (= 7.0.0)
|
|
||||||
- CapacitorFirebaseAuthentication/Lite (5.0.0):
|
|
||||||
- Capacitor
|
|
||||||
- FirebaseAuth (= 10.8.0)
|
|
||||||
- CapacitorHaptics (5.0.2):
|
- CapacitorHaptics (5.0.2):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorKeyboard (5.0.2):
|
- CapacitorKeyboard (5.0.2):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- CapacitorSplashScreen (5.0.2):
|
|
||||||
- Capacitor
|
|
||||||
- CapacitorStatusBar (5.0.2):
|
- CapacitorStatusBar (5.0.2):
|
||||||
- Capacitor
|
- Capacitor
|
||||||
- FirebaseAppCheckInterop (10.10.0)
|
|
||||||
- FirebaseAuth (10.8.0):
|
|
||||||
- FirebaseAppCheckInterop (~> 10.0)
|
|
||||||
- FirebaseCore (~> 10.0)
|
|
||||||
- GoogleUtilities/AppDelegateSwizzler (~> 7.8)
|
|
||||||
- GoogleUtilities/Environment (~> 7.8)
|
|
||||||
- GTMSessionFetcher/Core (< 4.0, >= 2.1)
|
|
||||||
- FirebaseCore (10.10.0):
|
|
||||||
- FirebaseCoreInternal (~> 10.0)
|
|
||||||
- GoogleUtilities/Environment (~> 7.8)
|
|
||||||
- GoogleUtilities/Logger (~> 7.8)
|
|
||||||
- FirebaseCoreInternal (10.10.0):
|
|
||||||
- "GoogleUtilities/NSData+zlib (~> 7.8)"
|
|
||||||
- GoogleSignIn (7.0.0):
|
|
||||||
- AppAuth (~> 1.5)
|
|
||||||
- GTMAppAuth (< 3.0, >= 1.3)
|
|
||||||
- GTMSessionFetcher/Core (< 4.0, >= 1.1)
|
|
||||||
- GoogleUtilities/AppDelegateSwizzler (7.11.1):
|
|
||||||
- GoogleUtilities/Environment
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- GoogleUtilities/Network
|
|
||||||
- GoogleUtilities/Environment (7.11.1):
|
|
||||||
- PromisesObjC (< 3.0, >= 1.2)
|
|
||||||
- GoogleUtilities/Logger (7.11.1):
|
|
||||||
- GoogleUtilities/Environment
|
|
||||||
- GoogleUtilities/Network (7.11.1):
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- "GoogleUtilities/NSData+zlib"
|
|
||||||
- GoogleUtilities/Reachability
|
|
||||||
- "GoogleUtilities/NSData+zlib (7.11.1)"
|
|
||||||
- GoogleUtilities/Reachability (7.11.1):
|
|
||||||
- GoogleUtilities/Logger
|
|
||||||
- GTMAppAuth (2.0.0):
|
|
||||||
- AppAuth/Core (~> 1.6)
|
|
||||||
- GTMSessionFetcher/Core (< 4.0, >= 1.5)
|
|
||||||
- GTMSessionFetcher/Core (3.1.1)
|
|
||||||
- PromisesObjC (2.2.0)
|
|
||||||
|
|
||||||
DEPENDENCIES:
|
DEPENDENCIES:
|
||||||
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
|
- "Capacitor (from `../../node_modules/@capacitor/ios`)"
|
||||||
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
|
- "CapacitorCordova (from `../../node_modules/@capacitor/ios`)"
|
||||||
- "CapacitorDialog (from `../../node_modules/@capacitor/dialog`)"
|
- "CapacitorDialog (from `../../node_modules/@capacitor/dialog`)"
|
||||||
- "CapacitorFirebaseAuthentication (from `../../node_modules/@capacitor-firebase/authentication`)"
|
|
||||||
- "CapacitorFirebaseAuthentication/Google (from `../../node_modules/@capacitor-firebase/authentication`)"
|
|
||||||
- "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
|
- "CapacitorHaptics (from `../../node_modules/@capacitor/haptics`)"
|
||||||
- "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)"
|
- "CapacitorKeyboard (from `../../node_modules/@capacitor/keyboard`)"
|
||||||
- "CapacitorSplashScreen (from `../../node_modules/@capacitor/splash-screen`)"
|
|
||||||
- "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
|
- "CapacitorStatusBar (from `../../node_modules/@capacitor/status-bar`)"
|
||||||
|
|
||||||
SPEC REPOS:
|
|
||||||
trunk:
|
|
||||||
- AppAuth
|
|
||||||
- FirebaseAppCheckInterop
|
|
||||||
- FirebaseAuth
|
|
||||||
- FirebaseCore
|
|
||||||
- FirebaseCoreInternal
|
|
||||||
- GoogleSignIn
|
|
||||||
- GoogleUtilities
|
|
||||||
- GTMAppAuth
|
|
||||||
- GTMSessionFetcher
|
|
||||||
- PromisesObjC
|
|
||||||
|
|
||||||
EXTERNAL SOURCES:
|
EXTERNAL SOURCES:
|
||||||
Capacitor:
|
Capacitor:
|
||||||
:path: "../../node_modules/@capacitor/ios"
|
:path: "../../node_modules/@capacitor/ios"
|
||||||
@@ -98,37 +26,21 @@ EXTERNAL SOURCES:
|
|||||||
:path: "../../node_modules/@capacitor/ios"
|
:path: "../../node_modules/@capacitor/ios"
|
||||||
CapacitorDialog:
|
CapacitorDialog:
|
||||||
:path: "../../node_modules/@capacitor/dialog"
|
:path: "../../node_modules/@capacitor/dialog"
|
||||||
CapacitorFirebaseAuthentication:
|
|
||||||
:path: "../../node_modules/@capacitor-firebase/authentication"
|
|
||||||
CapacitorHaptics:
|
CapacitorHaptics:
|
||||||
:path: "../../node_modules/@capacitor/haptics"
|
:path: "../../node_modules/@capacitor/haptics"
|
||||||
CapacitorKeyboard:
|
CapacitorKeyboard:
|
||||||
:path: "../../node_modules/@capacitor/keyboard"
|
:path: "../../node_modules/@capacitor/keyboard"
|
||||||
CapacitorSplashScreen:
|
|
||||||
:path: "../../node_modules/@capacitor/splash-screen"
|
|
||||||
CapacitorStatusBar:
|
CapacitorStatusBar:
|
||||||
:path: "../../node_modules/@capacitor/status-bar"
|
:path: "../../node_modules/@capacitor/status-bar"
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
SPEC CHECKSUMS:
|
||||||
AppAuth: 3bb1d1cd9340bd09f5ed189fb00b1cc28e1e8570
|
|
||||||
Capacitor: d3d4463573438b9fa65326d1f3549da6f4c21634
|
Capacitor: d3d4463573438b9fa65326d1f3549da6f4c21634
|
||||||
CapacitorCordova: b1fe6bf1f36974a8e4a9044b342d22d49c0996d6
|
CapacitorCordova: b1fe6bf1f36974a8e4a9044b342d22d49c0996d6
|
||||||
CapacitorDialog: 01c49f7f4b37e7ad59e38fd317a6e5f006f23cdc
|
CapacitorDialog: 01c49f7f4b37e7ad59e38fd317a6e5f006f23cdc
|
||||||
CapacitorFirebaseAuthentication: f2e3c2a7488b87078025855588670840f93a721e
|
|
||||||
CapacitorHaptics: 864585542a435bd41eaabf7f30d9ff5ec03024d3
|
CapacitorHaptics: 864585542a435bd41eaabf7f30d9ff5ec03024d3
|
||||||
CapacitorKeyboard: e628d4e66d621c69e449945ebabded17c5b9c2e8
|
CapacitorKeyboard: e628d4e66d621c69e449945ebabded17c5b9c2e8
|
||||||
CapacitorSplashScreen: bd2a056394ba0b8807e7bb3e746424f67c426e03
|
|
||||||
CapacitorStatusBar: 48f2899f6846cc7d8431b251ebfc58e1c10e3d58
|
CapacitorStatusBar: 48f2899f6846cc7d8431b251ebfc58e1c10e3d58
|
||||||
FirebaseAppCheckInterop: 7d3521f56872cf74a01792c0a095a30e054ff6ae
|
|
||||||
FirebaseAuth: 28e6fff787467cd15ab51c8c7aa904003b2f57aa
|
|
||||||
FirebaseCore: d027ff503d37edb78db98429b11f580a24a7df2a
|
|
||||||
FirebaseCoreInternal: 971029061d326000d65bfdc21f5502c75c8b0893
|
|
||||||
GoogleSignIn: b232380cf495a429b8095d3178a8d5855b42e842
|
|
||||||
GoogleUtilities: 9aa0ad5a7bc171f8bae016300bfcfa3fb8425749
|
|
||||||
GTMAppAuth: 99fb010047ba3973b7026e45393f51f27ab965ae
|
|
||||||
GTMSessionFetcher: e8647203b65cee28c5f73d0f473d096653945e72
|
|
||||||
PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef
|
|
||||||
|
|
||||||
PODFILE CHECKSUM: 9a19ff50409d024bca91266c62454036ebb27258
|
PODFILE CHECKSUM: b469cdc64593e190968b9aa15066224f10938107
|
||||||
|
|
||||||
COCOAPODS: 1.12.1
|
COCOAPODS: 1.12.1
|
||||||
|
|||||||
306
package-lock.json
generated
@@ -15,7 +15,6 @@
|
|||||||
"@capacitor/haptics": "^5.0.2",
|
"@capacitor/haptics": "^5.0.2",
|
||||||
"@capacitor/ios": "^5.0.4",
|
"@capacitor/ios": "^5.0.4",
|
||||||
"@capacitor/keyboard": "^5.0.2",
|
"@capacitor/keyboard": "^5.0.2",
|
||||||
"@capacitor/splash-screen": "^5.0.2",
|
|
||||||
"@capacitor/status-bar": "^5.0.2",
|
"@capacitor/status-bar": "^5.0.2",
|
||||||
"@ckeditor/ckeditor5-autoformat": "^37.1.0",
|
"@ckeditor/ckeditor5-autoformat": "^37.1.0",
|
||||||
"@ckeditor/ckeditor5-basic-styles": "^37.1.0",
|
"@ckeditor/ckeditor5-basic-styles": "^37.1.0",
|
||||||
@@ -35,6 +34,7 @@
|
|||||||
"@vueuse/core": "^10.1.2",
|
"@vueuse/core": "^10.1.2",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"cytoscape": "^3.25.0",
|
"cytoscape": "^3.25.0",
|
||||||
|
"daisyui": "^3.0.3",
|
||||||
"date-fns": "^2.29.3",
|
"date-fns": "^2.29.3",
|
||||||
"dompurify": "^3.0.2",
|
"dompurify": "^3.0.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
@@ -46,7 +46,8 @@
|
|||||||
"marked": "^4.3.0",
|
"marked": "^4.3.0",
|
||||||
"shortid": "^2.2.16",
|
"shortid": "^2.2.16",
|
||||||
"turndown": "^7.1.2",
|
"turndown": "^7.1.2",
|
||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4",
|
||||||
|
"vue-virtual-scroller": "^2.0.0-beta.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@capacitor/cli": "^5.0.4",
|
"@capacitor/cli": "^5.0.4",
|
||||||
@@ -65,7 +66,6 @@
|
|||||||
"@vue/eslint-config-typescript": "^11.0.3",
|
"@vue/eslint-config-typescript": "^11.0.3",
|
||||||
"@vue/tsconfig": "^0.4.0",
|
"@vue/tsconfig": "^0.4.0",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"daisyui": "^4.4.19",
|
|
||||||
"eslint": "^8.40.0",
|
"eslint": "^8.40.0",
|
||||||
"eslint-plugin-vue": "^9.13.0",
|
"eslint-plugin-vue": "^9.13.0",
|
||||||
"firebase-tools": "^12.1.0",
|
"firebase-tools": "^12.1.0",
|
||||||
@@ -86,7 +86,6 @@
|
|||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
},
|
},
|
||||||
@@ -328,14 +327,6 @@
|
|||||||
"@capacitor/core": "^5.0.0"
|
"@capacitor/core": "^5.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@capacitor/splash-screen": {
|
|
||||||
"version": "5.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor/splash-screen/-/splash-screen-5.0.2.tgz",
|
|
||||||
"integrity": "sha512-DJQQL3VfY7LfMH0FJgzrb0N1v3fZw0EVfBRB+xQjbI13jjoafS8WPRCQDvXxzoJqfh5hQyepDRyYxIdOCB2KEA==",
|
|
||||||
"peerDependencies": {
|
|
||||||
"@capacitor/core": "^5.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@capacitor/status-bar": {
|
"node_modules/@capacitor/status-bar": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-5.0.2.tgz",
|
||||||
@@ -2084,7 +2075,6 @@
|
|||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/set-array": "^1.0.1",
|
"@jridgewell/set-array": "^1.0.1",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||||
@@ -2098,7 +2088,6 @@
|
|||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
@@ -2107,7 +2096,6 @@
|
|||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.0.0"
|
"node": ">=6.0.0"
|
||||||
}
|
}
|
||||||
@@ -2121,7 +2109,6 @@
|
|||||||
"version": "0.3.18",
|
"version": "0.3.18",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
||||||
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/resolve-uri": "3.1.0",
|
"@jridgewell/resolve-uri": "3.1.0",
|
||||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||||
@@ -2130,8 +2117,7 @@
|
|||||||
"node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
|
"node_modules/@jridgewell/trace-mapping/node_modules/@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.4.14",
|
"version": "1.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/@jsdevtools/ono": {
|
"node_modules/@jsdevtools/ono": {
|
||||||
"version": "7.1.3",
|
"version": "7.1.3",
|
||||||
@@ -3372,14 +3358,12 @@
|
|||||||
"node_modules/any-promise": {
|
"node_modules/any-promise": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||||
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
|
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/anymatch": {
|
"node_modules/anymatch": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"picomatch": "^2.0.4"
|
"picomatch": "^2.0.4"
|
||||||
@@ -3481,8 +3465,7 @@
|
|||||||
"node_modules/arg": {
|
"node_modules/arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/argparse": {
|
"node_modules/argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@@ -3662,8 +3645,7 @@
|
|||||||
"node_modules/balanced-match": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/base64-js": {
|
"node_modules/base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
@@ -3740,7 +3722,6 @@
|
|||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@@ -4148,7 +4129,6 @@
|
|||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
||||||
"dev": true,
|
|
||||||
"funding": [
|
"funding": [
|
||||||
{
|
{
|
||||||
"type": "individual",
|
"type": "individual",
|
||||||
@@ -4359,6 +4339,11 @@
|
|||||||
"color-support": "bin.js"
|
"color-support": "bin.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/colord": {
|
||||||
|
"version": "2.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
|
||||||
|
"integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
|
||||||
|
},
|
||||||
"node_modules/colorette": {
|
"node_modules/colorette": {
|
||||||
"version": "2.0.20",
|
"version": "2.0.20",
|
||||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||||
@@ -4425,7 +4410,6 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
@@ -4508,8 +4492,7 @@
|
|||||||
"node_modules/concat-map": {
|
"node_modules/concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/config-chain": {
|
"node_modules/config-chain": {
|
||||||
"version": "1.1.13",
|
"version": "1.1.13",
|
||||||
@@ -4814,7 +4797,6 @@
|
|||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz",
|
||||||
"integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==",
|
"integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cssesc": "^3.0.0",
|
"cssesc": "^3.0.0",
|
||||||
"fastparse": "^1.1.2"
|
"fastparse": "^1.1.2"
|
||||||
@@ -4842,15 +4824,6 @@
|
|||||||
"integrity": "sha512-cTXY6iy0gN5Ha/cGILeDgQE+nKiKDU2m0DjSRdJhr86BN3cM7oefBsTk2aH0LQeaYtL7Z7HvW+jYoadmdhzeDA==",
|
"integrity": "sha512-cTXY6iy0gN5Ha/cGILeDgQE+nKiKDU2m0DjSRdJhr86BN3cM7oefBsTk2aH0LQeaYtL7Z7HvW+jYoadmdhzeDA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"node_modules/culori": {
|
|
||||||
"version": "3.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz",
|
|
||||||
"integrity": "sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==",
|
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
|
||||||
"node": "^12.20.0 || ^14.13.1 || >=16.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cytoscape": {
|
"node_modules/cytoscape": {
|
||||||
"version": "3.25.0",
|
"version": "3.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.25.0.tgz",
|
||||||
@@ -4864,22 +4837,21 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/daisyui": {
|
"node_modules/daisyui": {
|
||||||
"version": "4.4.19",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.0.3.tgz",
|
||||||
"integrity": "sha512-IjOLWwnndD4N7Ut5CDxbUsaVtbqXPeVHM92IcgxGFxpuOd3CCKW/PAXZH6JoBTHFRaN57vB9XqEhdWm5yC+bPA==",
|
"integrity": "sha512-RSbXsEBj2LonvjOKEI0I64F5xFJrFrthPgxRNeAZKmACQ3NoIoP45lO6UXLW3bm8PVOUGpKf1Br2SWwc1NqnHQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"colord": "^2.9",
|
||||||
"css-selector-tokenizer": "^0.8",
|
"css-selector-tokenizer": "^0.8",
|
||||||
"culori": "^3",
|
"postcss-js": "^4",
|
||||||
"picocolors": "^1",
|
"tailwindcss": "^3"
|
||||||
"postcss-js": "^4"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=16.9.0"
|
|
||||||
},
|
},
|
||||||
"funding": {
|
"funding": {
|
||||||
"type": "opencollective",
|
"type": "opencollective",
|
||||||
"url": "https://opencollective.com/daisyui"
|
"url": "https://opencollective.com/daisyui"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"postcss": "^8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/dashdash": {
|
"node_modules/dashdash": {
|
||||||
@@ -5054,8 +5026,7 @@
|
|||||||
"node_modules/didyoumean": {
|
"node_modules/didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/dir-glob": {
|
"node_modules/dir-glob": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
@@ -5072,8 +5043,7 @@
|
|||||||
"node_modules/dlv": {
|
"node_modules/dlv": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/doctrine": {
|
"node_modules/doctrine": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@@ -6068,8 +6038,7 @@
|
|||||||
"node_modules/fastparse": {
|
"node_modules/fastparse": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
||||||
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
|
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/fastq": {
|
"node_modules/fastq": {
|
||||||
"version": "1.15.0",
|
"version": "1.15.0",
|
||||||
@@ -6519,14 +6488,12 @@
|
|||||||
"node_modules/fs.realpath": {
|
"node_modules/fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/fsevents": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
"dev": true,
|
|
||||||
"hasInstallScript": true,
|
"hasInstallScript": true,
|
||||||
"optional": true,
|
"optional": true,
|
||||||
"os": [
|
"os": [
|
||||||
@@ -6755,7 +6722,6 @@
|
|||||||
"version": "7.1.6",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
"inflight": "^1.0.4",
|
"inflight": "^1.0.4",
|
||||||
@@ -6803,7 +6769,6 @@
|
|||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@@ -6813,7 +6778,6 @@
|
|||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
},
|
},
|
||||||
@@ -7582,7 +7546,6 @@
|
|||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"once": "^1.3.0",
|
"once": "^1.3.0",
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
@@ -7713,7 +7676,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"binary-extensions": "^2.0.0"
|
"binary-extensions": "^2.0.0"
|
||||||
},
|
},
|
||||||
@@ -8117,7 +8079,6 @@
|
|||||||
"version": "1.18.2",
|
"version": "1.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
|
||||||
"integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==",
|
"integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==",
|
||||||
"dev": true,
|
|
||||||
"bin": {
|
"bin": {
|
||||||
"jiti": "bin/jiti.js"
|
"jiti": "bin/jiti.js"
|
||||||
}
|
}
|
||||||
@@ -8524,7 +8485,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||||
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
|
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
@@ -8532,8 +8492,7 @@
|
|||||||
"node_modules/lines-and-columns": {
|
"node_modules/lines-and-columns": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/linkify-it": {
|
"node_modules/linkify-it": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
@@ -9160,6 +9119,11 @@
|
|||||||
"node": ">= 8"
|
"node": ">= 8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/mitt": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
|
||||||
|
},
|
||||||
"node_modules/mkdirp": {
|
"node_modules/mkdirp": {
|
||||||
"version": "0.5.6",
|
"version": "0.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
@@ -9249,7 +9213,6 @@
|
|||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||||
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"any-promise": "^1.0.0",
|
"any-promise": "^1.0.0",
|
||||||
"object-assign": "^4.0.1",
|
"object-assign": "^4.0.1",
|
||||||
@@ -9459,7 +9422,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -9699,7 +9661,6 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -9708,7 +9669,6 @@
|
|||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
@@ -9774,7 +9734,6 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@@ -10014,7 +9973,6 @@
|
|||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
@@ -10140,7 +10098,6 @@
|
|||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
|
||||||
"integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
|
"integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 6"
|
"node": ">= 6"
|
||||||
}
|
}
|
||||||
@@ -10232,7 +10189,6 @@
|
|||||||
"version": "15.1.0",
|
"version": "15.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
||||||
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"postcss-value-parser": "^4.0.0",
|
"postcss-value-parser": "^4.0.0",
|
||||||
"read-cache": "^1.0.0",
|
"read-cache": "^1.0.0",
|
||||||
@@ -10267,7 +10223,6 @@
|
|||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz",
|
||||||
"integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==",
|
"integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lilconfig": "^2.0.5",
|
"lilconfig": "^2.0.5",
|
||||||
"yaml": "^2.1.1"
|
"yaml": "^2.1.1"
|
||||||
@@ -10317,7 +10272,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
|
||||||
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
|
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"postcss-selector-parser": "^6.0.11"
|
"postcss-selector-parser": "^6.0.11"
|
||||||
},
|
},
|
||||||
@@ -10908,7 +10862,6 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"picomatch": "^2.2.1"
|
"picomatch": "^2.2.1"
|
||||||
},
|
},
|
||||||
@@ -11861,7 +11814,6 @@
|
|||||||
"version": "3.32.0",
|
"version": "3.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
|
||||||
"integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==",
|
"integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@jridgewell/gen-mapping": "^0.3.2",
|
"@jridgewell/gen-mapping": "^0.3.2",
|
||||||
"commander": "^4.0.0",
|
"commander": "^4.0.0",
|
||||||
@@ -12008,7 +11960,6 @@
|
|||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz",
|
||||||
"integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==",
|
"integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
@@ -12046,7 +11997,6 @@
|
|||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"is-glob": "^4.0.3"
|
"is-glob": "^4.0.3"
|
||||||
},
|
},
|
||||||
@@ -12151,7 +12101,6 @@
|
|||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||||
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"any-promise": "^1.0.0"
|
"any-promise": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -12160,7 +12109,6 @@
|
|||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
||||||
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"thenify": ">= 3.1.0 < 4"
|
"thenify": ">= 3.1.0 < 4"
|
||||||
},
|
},
|
||||||
@@ -12260,8 +12208,7 @@
|
|||||||
"node_modules/ts-interface-checker": {
|
"node_modules/ts-interface-checker": {
|
||||||
"version": "0.1.13",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/tslib": {
|
"node_modules/tslib": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
@@ -12886,6 +12833,22 @@
|
|||||||
"node": ">=4.0"
|
"node": ">=4.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-observe-visibility": {
|
||||||
|
"version": "2.0.0-alpha.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-observe-visibility/-/vue-observe-visibility-2.0.0-alpha.1.tgz",
|
||||||
|
"integrity": "sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/vue-resize": {
|
||||||
|
"version": "2.0.0-alpha.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-2.0.0-alpha.1.tgz",
|
||||||
|
"integrity": "sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-template-compiler": {
|
"node_modules/vue-template-compiler": {
|
||||||
"version": "2.7.14",
|
"version": "2.7.14",
|
||||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
|
||||||
@@ -12913,6 +12876,19 @@
|
|||||||
"typescript": "*"
|
"typescript": "*"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-virtual-scroller": {
|
||||||
|
"version": "2.0.0-beta.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-virtual-scroller/-/vue-virtual-scroller-2.0.0-beta.8.tgz",
|
||||||
|
"integrity": "sha512-b8/f5NQ5nIEBRTNi6GcPItE4s7kxNHw2AIHLtDp+2QvqdTjVN0FgONwX9cr53jWRgnu+HRLPaWDOR2JPI5MTfQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"mitt": "^2.1.0",
|
||||||
|
"vue-observe-visibility": "^2.0.0-alpha.1",
|
||||||
|
"vue-resize": "^2.0.0-alpha.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/wcwidth": {
|
"node_modules/wcwidth": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||||
@@ -13115,8 +13091,7 @@
|
|||||||
"node_modules/wrappy": {
|
"node_modules/wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/write-file-atomic": {
|
"node_modules/write-file-atomic": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
@@ -13233,7 +13208,6 @@
|
|||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
|
||||||
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
|
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">= 14"
|
"node": ">= 14"
|
||||||
}
|
}
|
||||||
@@ -13304,8 +13278,7 @@
|
|||||||
"@alloc/quick-lru": {
|
"@alloc/quick-lru": {
|
||||||
"version": "5.2.0",
|
"version": "5.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
|
||||||
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
|
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@antfu/utils": {
|
"@antfu/utils": {
|
||||||
"version": "0.7.2",
|
"version": "0.7.2",
|
||||||
@@ -13463,12 +13436,6 @@
|
|||||||
"integrity": "sha512-5qauopAd9Dlltzi87i/WHMBsocCf0BJqVxRZ3H+kifQm4BLHGokrzuanddEJd6MPOnglSl3H3C9oElJB3wkVVQ==",
|
"integrity": "sha512-5qauopAd9Dlltzi87i/WHMBsocCf0BJqVxRZ3H+kifQm4BLHGokrzuanddEJd6MPOnglSl3H3C9oElJB3wkVVQ==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"@capacitor/splash-screen": {
|
|
||||||
"version": "5.0.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor/splash-screen/-/splash-screen-5.0.2.tgz",
|
|
||||||
"integrity": "sha512-DJQQL3VfY7LfMH0FJgzrb0N1v3fZw0EVfBRB+xQjbI13jjoafS8WPRCQDvXxzoJqfh5hQyepDRyYxIdOCB2KEA==",
|
|
||||||
"requires": {}
|
|
||||||
},
|
|
||||||
"@capacitor/status-bar": {
|
"@capacitor/status-bar": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/@capacitor/status-bar/-/status-bar-5.0.2.tgz",
|
||||||
@@ -14727,7 +14694,6 @@
|
|||||||
"version": "0.3.3",
|
"version": "0.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz",
|
||||||
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
"integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/set-array": "^1.0.1",
|
"@jridgewell/set-array": "^1.0.1",
|
||||||
"@jridgewell/sourcemap-codec": "^1.4.10",
|
"@jridgewell/sourcemap-codec": "^1.4.10",
|
||||||
@@ -14737,14 +14703,12 @@
|
|||||||
"@jridgewell/resolve-uri": {
|
"@jridgewell/resolve-uri": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.0.tgz",
|
||||||
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==",
|
"integrity": "sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@jridgewell/set-array": {
|
"@jridgewell/set-array": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz",
|
||||||
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==",
|
"integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@jridgewell/sourcemap-codec": {
|
"@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.4.15",
|
"version": "1.4.15",
|
||||||
@@ -14755,7 +14719,6 @@
|
|||||||
"version": "0.3.18",
|
"version": "0.3.18",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.18.tgz",
|
||||||
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
"integrity": "sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/resolve-uri": "3.1.0",
|
"@jridgewell/resolve-uri": "3.1.0",
|
||||||
"@jridgewell/sourcemap-codec": "1.4.14"
|
"@jridgewell/sourcemap-codec": "1.4.14"
|
||||||
@@ -14764,8 +14727,7 @@
|
|||||||
"@jridgewell/sourcemap-codec": {
|
"@jridgewell/sourcemap-codec": {
|
||||||
"version": "1.4.14",
|
"version": "1.4.14",
|
||||||
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz",
|
||||||
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==",
|
"integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw=="
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -15705,14 +15667,12 @@
|
|||||||
"any-promise": {
|
"any-promise": {
|
||||||
"version": "1.3.0",
|
"version": "1.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
|
||||||
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
|
"integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"anymatch": {
|
"anymatch": {
|
||||||
"version": "3.1.3",
|
"version": "3.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
|
||||||
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
"integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"normalize-path": "^3.0.0",
|
"normalize-path": "^3.0.0",
|
||||||
"picomatch": "^2.0.4"
|
"picomatch": "^2.0.4"
|
||||||
@@ -15804,8 +15764,7 @@
|
|||||||
"arg": {
|
"arg": {
|
||||||
"version": "5.0.2",
|
"version": "5.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
|
||||||
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
|
"integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"argparse": {
|
"argparse": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
@@ -15936,8 +15895,7 @@
|
|||||||
"balanced-match": {
|
"balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
|
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"base64-js": {
|
"base64-js": {
|
||||||
"version": "1.5.1",
|
"version": "1.5.1",
|
||||||
@@ -15992,8 +15950,7 @@
|
|||||||
"binary-extensions": {
|
"binary-extensions": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz",
|
||||||
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==",
|
"integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"bl": {
|
"bl": {
|
||||||
"version": "4.1.0",
|
"version": "4.1.0",
|
||||||
@@ -16288,7 +16245,6 @@
|
|||||||
"version": "3.5.3",
|
"version": "3.5.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
|
||||||
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
"integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"anymatch": "~3.1.2",
|
"anymatch": "~3.1.2",
|
||||||
"braces": "~3.0.2",
|
"braces": "~3.0.2",
|
||||||
@@ -16440,6 +16396,11 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
|
"colord": {
|
||||||
|
"version": "2.9.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz",
|
||||||
|
"integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw=="
|
||||||
|
},
|
||||||
"colorette": {
|
"colorette": {
|
||||||
"version": "2.0.20",
|
"version": "2.0.20",
|
||||||
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
"resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
|
||||||
@@ -16501,8 +16462,7 @@
|
|||||||
"commander": {
|
"commander": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
|
||||||
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
|
"integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"compress-commons": {
|
"compress-commons": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
@@ -16572,8 +16532,7 @@
|
|||||||
"concat-map": {
|
"concat-map": {
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
|
||||||
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
|
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"config-chain": {
|
"config-chain": {
|
||||||
"version": "1.1.13",
|
"version": "1.1.13",
|
||||||
@@ -16817,7 +16776,6 @@
|
|||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/css-selector-tokenizer/-/css-selector-tokenizer-0.8.0.tgz",
|
||||||
"integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==",
|
"integrity": "sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"cssesc": "^3.0.0",
|
"cssesc": "^3.0.0",
|
||||||
"fastparse": "^1.1.2"
|
"fastparse": "^1.1.2"
|
||||||
@@ -16839,12 +16797,6 @@
|
|||||||
"integrity": "sha512-cTXY6iy0gN5Ha/cGILeDgQE+nKiKDU2m0DjSRdJhr86BN3cM7oefBsTk2aH0LQeaYtL7Z7HvW+jYoadmdhzeDA==",
|
"integrity": "sha512-cTXY6iy0gN5Ha/cGILeDgQE+nKiKDU2m0DjSRdJhr86BN3cM7oefBsTk2aH0LQeaYtL7Z7HvW+jYoadmdhzeDA==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"culori": {
|
|
||||||
"version": "3.3.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/culori/-/culori-3.3.0.tgz",
|
|
||||||
"integrity": "sha512-pHJg+jbuFsCjz9iclQBqyL3B2HLCBF71BwVNujUYEvCeQMvV97R59MNK3R2+jgJ3a1fcZgI9B3vYgz8lzr/BFQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"cytoscape": {
|
"cytoscape": {
|
||||||
"version": "3.25.0",
|
"version": "3.25.0",
|
||||||
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.25.0.tgz",
|
"resolved": "https://registry.npmjs.org/cytoscape/-/cytoscape-3.25.0.tgz",
|
||||||
@@ -16855,15 +16807,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"daisyui": {
|
"daisyui": {
|
||||||
"version": "4.4.19",
|
"version": "3.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-4.4.19.tgz",
|
"resolved": "https://registry.npmjs.org/daisyui/-/daisyui-3.0.3.tgz",
|
||||||
"integrity": "sha512-IjOLWwnndD4N7Ut5CDxbUsaVtbqXPeVHM92IcgxGFxpuOd3CCKW/PAXZH6JoBTHFRaN57vB9XqEhdWm5yC+bPA==",
|
"integrity": "sha512-RSbXsEBj2LonvjOKEI0I64F5xFJrFrthPgxRNeAZKmACQ3NoIoP45lO6UXLW3bm8PVOUGpKf1Br2SWwc1NqnHQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"colord": "^2.9",
|
||||||
"css-selector-tokenizer": "^0.8",
|
"css-selector-tokenizer": "^0.8",
|
||||||
"culori": "^3",
|
"postcss-js": "^4",
|
||||||
"picocolors": "^1",
|
"tailwindcss": "^3"
|
||||||
"postcss-js": "^4"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"dashdash": {
|
"dashdash": {
|
||||||
@@ -16989,8 +16940,7 @@
|
|||||||
"didyoumean": {
|
"didyoumean": {
|
||||||
"version": "1.2.2",
|
"version": "1.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
|
||||||
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
|
"integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"dir-glob": {
|
"dir-glob": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
@@ -17004,8 +16954,7 @@
|
|||||||
"dlv": {
|
"dlv": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
|
||||||
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
|
"integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"doctrine": {
|
"doctrine": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@@ -17811,8 +17760,7 @@
|
|||||||
"fastparse": {
|
"fastparse": {
|
||||||
"version": "1.1.2",
|
"version": "1.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/fastparse/-/fastparse-1.1.2.tgz",
|
||||||
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==",
|
"integrity": "sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"fastq": {
|
"fastq": {
|
||||||
"version": "1.15.0",
|
"version": "1.15.0",
|
||||||
@@ -18189,14 +18137,12 @@
|
|||||||
"fs.realpath": {
|
"fs.realpath": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
|
||||||
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
|
"integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"fsevents": {
|
"fsevents": {
|
||||||
"version": "2.3.2",
|
"version": "2.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||||
"dev": true,
|
|
||||||
"optional": true
|
"optional": true
|
||||||
},
|
},
|
||||||
"ftp": {
|
"ftp": {
|
||||||
@@ -18380,7 +18326,6 @@
|
|||||||
"version": "7.1.6",
|
"version": "7.1.6",
|
||||||
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
|
||||||
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"fs.realpath": "^1.0.0",
|
"fs.realpath": "^1.0.0",
|
||||||
"inflight": "^1.0.4",
|
"inflight": "^1.0.4",
|
||||||
@@ -18394,7 +18339,6 @@
|
|||||||
"version": "1.1.11",
|
"version": "1.1.11",
|
||||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
|
||||||
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
"integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"balanced-match": "^1.0.0",
|
"balanced-match": "^1.0.0",
|
||||||
"concat-map": "0.0.1"
|
"concat-map": "0.0.1"
|
||||||
@@ -18404,7 +18348,6 @@
|
|||||||
"version": "3.1.2",
|
"version": "3.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
|
||||||
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
"integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
@@ -18999,7 +18942,6 @@
|
|||||||
"version": "1.0.6",
|
"version": "1.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
|
||||||
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
"integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"once": "^1.3.0",
|
"once": "^1.3.0",
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
@@ -19105,7 +19047,6 @@
|
|||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
"integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"binary-extensions": "^2.0.0"
|
"binary-extensions": "^2.0.0"
|
||||||
}
|
}
|
||||||
@@ -19379,8 +19320,7 @@
|
|||||||
"jiti": {
|
"jiti": {
|
||||||
"version": "1.18.2",
|
"version": "1.18.2",
|
||||||
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
|
"resolved": "https://registry.npmjs.org/jiti/-/jiti-1.18.2.tgz",
|
||||||
"integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg==",
|
"integrity": "sha512-QAdOptna2NYiSSpv0O/BwoHBSmz4YhpzJHyi+fnMRTXFjp7B8i/YG5Z8IfusxB1ufjcD2Sre1F3R+nX3fvy7gg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"jju": {
|
"jju": {
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
@@ -19748,14 +19688,12 @@
|
|||||||
"lilconfig": {
|
"lilconfig": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
|
||||||
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
|
"integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"lines-and-columns": {
|
"lines-and-columns": {
|
||||||
"version": "1.2.4",
|
"version": "1.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
|
||||||
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
|
"integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"linkify-it": {
|
"linkify-it": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
@@ -20233,6 +20171,11 @@
|
|||||||
"yallist": "^4.0.0"
|
"yallist": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mitt": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mitt/-/mitt-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-ILj2TpLiysu2wkBbWjAmww7TkZb65aiQO+DkVdUTBpBXq+MHYiETENkKFMtsJZX1Lf4pe4QOrTSjIfUwN5lRdg=="
|
||||||
|
},
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "0.5.6",
|
"version": "0.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
|
||||||
@@ -20315,7 +20258,6 @@
|
|||||||
"version": "2.7.0",
|
"version": "2.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
|
||||||
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
"integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"any-promise": "^1.0.0",
|
"any-promise": "^1.0.0",
|
||||||
"object-assign": "^4.0.1",
|
"object-assign": "^4.0.1",
|
||||||
@@ -20472,8 +20414,7 @@
|
|||||||
"normalize-path": {
|
"normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
|
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"normalize-range": {
|
"normalize-range": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
@@ -20655,14 +20596,12 @@
|
|||||||
"object-assign": {
|
"object-assign": {
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"object-hash": {
|
"object-hash": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
|
||||||
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
|
"integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"object-inspect": {
|
"object-inspect": {
|
||||||
"version": "1.12.3",
|
"version": "1.12.3",
|
||||||
@@ -20707,7 +20646,6 @@
|
|||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"wrappy": "1"
|
"wrappy": "1"
|
||||||
}
|
}
|
||||||
@@ -20886,8 +20824,7 @@
|
|||||||
"path-is-absolute": {
|
"path-is-absolute": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
|
||||||
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
|
"integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"path-key": {
|
"path-key": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
@@ -20978,8 +20915,7 @@
|
|||||||
"pirates": {
|
"pirates": {
|
||||||
"version": "4.0.5",
|
"version": "4.0.5",
|
||||||
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.5.tgz",
|
||||||
"integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ==",
|
"integrity": "sha512-8V9+HQPupnaXMA23c5hvl69zXvTwTzyAYasnkb0Tts4XvO4CliqONMOnvlq26rkhLC3nWDFBJf73LU1e1VZLaQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"pkg-types": {
|
"pkg-types": {
|
||||||
"version": "1.0.3",
|
"version": "1.0.3",
|
||||||
@@ -21047,7 +20983,6 @@
|
|||||||
"version": "15.1.0",
|
"version": "15.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
|
||||||
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
"integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"postcss-value-parser": "^4.0.0",
|
"postcss-value-parser": "^4.0.0",
|
||||||
"read-cache": "^1.0.0",
|
"read-cache": "^1.0.0",
|
||||||
@@ -21066,7 +21001,6 @@
|
|||||||
"version": "4.0.1",
|
"version": "4.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.1.tgz",
|
||||||
"integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==",
|
"integrity": "sha512-vEJIc8RdiBRu3oRAI0ymerOn+7rPuMvRXslTvZUKZonDHFIczxztIyJ1urxM1x9JXEikvpWWTUUqal5j/8QgvA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"lilconfig": "^2.0.5",
|
"lilconfig": "^2.0.5",
|
||||||
"yaml": "^2.1.1"
|
"yaml": "^2.1.1"
|
||||||
@@ -21087,7 +21021,6 @@
|
|||||||
"version": "6.0.1",
|
"version": "6.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz",
|
||||||
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
|
"integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"postcss-selector-parser": "^6.0.11"
|
"postcss-selector-parser": "^6.0.11"
|
||||||
}
|
}
|
||||||
@@ -21495,7 +21428,6 @@
|
|||||||
"version": "3.6.0",
|
"version": "3.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
|
||||||
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
"integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"picomatch": "^2.2.1"
|
"picomatch": "^2.2.1"
|
||||||
}
|
}
|
||||||
@@ -22228,7 +22160,6 @@
|
|||||||
"version": "3.32.0",
|
"version": "3.32.0",
|
||||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
|
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.32.0.tgz",
|
||||||
"integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==",
|
"integrity": "sha512-ydQOU34rpSyj2TGyz4D2p8rbktIOZ8QY9s+DGLvFU1i5pWJE8vkpruCjGCMHsdXwnD7JDcS+noSwM/a7zyNFDQ==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@jridgewell/gen-mapping": "^0.3.2",
|
"@jridgewell/gen-mapping": "^0.3.2",
|
||||||
"commander": "^4.0.0",
|
"commander": "^4.0.0",
|
||||||
@@ -22332,7 +22263,6 @@
|
|||||||
"version": "3.3.2",
|
"version": "3.3.2",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.2.tgz",
|
||||||
"integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==",
|
"integrity": "sha512-9jPkMiIBXvPc2KywkraqsUfbfj+dHDb+JPWtSJa9MLFdrPyazI7q6WX2sUrm7R9eVR7qqv3Pas7EvQFzxKnI6w==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@alloc/quick-lru": "^5.2.0",
|
"@alloc/quick-lru": "^5.2.0",
|
||||||
"arg": "^5.0.2",
|
"arg": "^5.0.2",
|
||||||
@@ -22363,7 +22293,6 @@
|
|||||||
"version": "6.0.2",
|
"version": "6.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
|
||||||
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"is-glob": "^4.0.3"
|
"is-glob": "^4.0.3"
|
||||||
}
|
}
|
||||||
@@ -22448,7 +22377,6 @@
|
|||||||
"version": "3.3.1",
|
"version": "3.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
|
||||||
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
"integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"any-promise": "^1.0.0"
|
"any-promise": "^1.0.0"
|
||||||
}
|
}
|
||||||
@@ -22457,7 +22385,6 @@
|
|||||||
"version": "1.6.0",
|
"version": "1.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
|
||||||
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
"integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"thenify": ">= 3.1.0 < 4"
|
"thenify": ">= 3.1.0 < 4"
|
||||||
}
|
}
|
||||||
@@ -22539,8 +22466,7 @@
|
|||||||
"ts-interface-checker": {
|
"ts-interface-checker": {
|
||||||
"version": "0.1.13",
|
"version": "0.1.13",
|
||||||
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
"resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
|
||||||
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
|
"integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"tslib": {
|
"tslib": {
|
||||||
"version": "2.5.0",
|
"version": "2.5.0",
|
||||||
@@ -22972,6 +22898,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"vue-observe-visibility": {
|
||||||
|
"version": "2.0.0-alpha.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-observe-visibility/-/vue-observe-visibility-2.0.0-alpha.1.tgz",
|
||||||
|
"integrity": "sha512-flFbp/gs9pZniXR6fans8smv1kDScJ8RS7rEpMjhVabiKeq7Qz3D9+eGsypncjfIyyU84saU88XZ0zjbD6Gq/g==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
|
"vue-resize": {
|
||||||
|
"version": "2.0.0-alpha.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-resize/-/vue-resize-2.0.0-alpha.1.tgz",
|
||||||
|
"integrity": "sha512-7+iqOueLU7uc9NrMfrzbG8hwMqchfVfSzpVlCMeJQe4pyibqyoifDNbKTZvwxZKDvGkB+PdFeKvnGZMoEb8esg==",
|
||||||
|
"requires": {}
|
||||||
|
},
|
||||||
"vue-template-compiler": {
|
"vue-template-compiler": {
|
||||||
"version": "2.7.14",
|
"version": "2.7.14",
|
||||||
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
|
"resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.14.tgz",
|
||||||
@@ -22993,6 +22931,16 @@
|
|||||||
"semver": "^7.3.8"
|
"semver": "^7.3.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"vue-virtual-scroller": {
|
||||||
|
"version": "2.0.0-beta.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/vue-virtual-scroller/-/vue-virtual-scroller-2.0.0-beta.8.tgz",
|
||||||
|
"integrity": "sha512-b8/f5NQ5nIEBRTNi6GcPItE4s7kxNHw2AIHLtDp+2QvqdTjVN0FgONwX9cr53jWRgnu+HRLPaWDOR2JPI5MTfQ==",
|
||||||
|
"requires": {
|
||||||
|
"mitt": "^2.1.0",
|
||||||
|
"vue-observe-visibility": "^2.0.0-alpha.1",
|
||||||
|
"vue-resize": "^2.0.0-alpha.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"wcwidth": {
|
"wcwidth": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz",
|
||||||
@@ -23153,8 +23101,7 @@
|
|||||||
"wrappy": {
|
"wrappy": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"write-file-atomic": {
|
"write-file-atomic": {
|
||||||
"version": "3.0.3",
|
"version": "3.0.3",
|
||||||
@@ -23237,8 +23184,7 @@
|
|||||||
"yaml": {
|
"yaml": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
|
||||||
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
|
"integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"yargs": {
|
"yargs": {
|
||||||
"version": "16.2.0",
|
"version": "16.2.0",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@
|
|||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"build-only": "vite build",
|
"build-only": "vite build",
|
||||||
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
"type-check": "vue-tsc --noEmit -p tsconfig.app.json --composite false",
|
||||||
"lint": "eslint ./src --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
|
||||||
"format": "prettier --write src/",
|
"format": "prettier --write src/",
|
||||||
"local": "npm run build && firebase serve --only hosting",
|
"local": "npm run build && firebase serve --only hosting",
|
||||||
"deploy": "npm run build && firebase deploy --only hosting:contexted-v3"
|
"deploy": "npm run build && firebase deploy --only hosting:contexted-v3"
|
||||||
@@ -22,7 +22,6 @@
|
|||||||
"@capacitor/haptics": "^5.0.2",
|
"@capacitor/haptics": "^5.0.2",
|
||||||
"@capacitor/ios": "^5.0.4",
|
"@capacitor/ios": "^5.0.4",
|
||||||
"@capacitor/keyboard": "^5.0.2",
|
"@capacitor/keyboard": "^5.0.2",
|
||||||
"@capacitor/splash-screen": "^5.0.2",
|
|
||||||
"@capacitor/status-bar": "^5.0.2",
|
"@capacitor/status-bar": "^5.0.2",
|
||||||
"@ckeditor/ckeditor5-autoformat": "^37.1.0",
|
"@ckeditor/ckeditor5-autoformat": "^37.1.0",
|
||||||
"@ckeditor/ckeditor5-basic-styles": "^37.1.0",
|
"@ckeditor/ckeditor5-basic-styles": "^37.1.0",
|
||||||
@@ -42,6 +41,7 @@
|
|||||||
"@vueuse/core": "^10.1.2",
|
"@vueuse/core": "^10.1.2",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"cytoscape": "^3.25.0",
|
"cytoscape": "^3.25.0",
|
||||||
|
"daisyui": "^3.0.3",
|
||||||
"date-fns": "^2.29.3",
|
"date-fns": "^2.29.3",
|
||||||
"dompurify": "^3.0.2",
|
"dompurify": "^3.0.2",
|
||||||
"file-saver": "^2.0.5",
|
"file-saver": "^2.0.5",
|
||||||
@@ -53,7 +53,8 @@
|
|||||||
"marked": "^4.3.0",
|
"marked": "^4.3.0",
|
||||||
"shortid": "^2.2.16",
|
"shortid": "^2.2.16",
|
||||||
"turndown": "^7.1.2",
|
"turndown": "^7.1.2",
|
||||||
"vue": "^3.3.4"
|
"vue": "^3.3.4",
|
||||||
|
"vue-virtual-scroller": "^2.0.0-beta.8"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@capacitor/cli": "^5.0.4",
|
"@capacitor/cli": "^5.0.4",
|
||||||
@@ -72,7 +73,6 @@
|
|||||||
"@vue/eslint-config-typescript": "^11.0.3",
|
"@vue/eslint-config-typescript": "^11.0.3",
|
||||||
"@vue/tsconfig": "^0.4.0",
|
"@vue/tsconfig": "^0.4.0",
|
||||||
"autoprefixer": "^10.4.14",
|
"autoprefixer": "^10.4.14",
|
||||||
"daisyui": "^4.4.19",
|
|
||||||
"eslint": "^8.40.0",
|
"eslint": "^8.40.0",
|
||||||
"eslint-plugin-vue": "^9.13.0",
|
"eslint-plugin-vue": "^9.13.0",
|
||||||
"firebase-tools": "^12.1.0",
|
"firebase-tools": "^12.1.0",
|
||||||
|
|||||||
187
src/App.vue
@@ -2,22 +2,19 @@
|
|||||||
import { activeNote, updateNote, notes, activeNotesSource, getNotes } from '@/composables/useNotes'
|
import { activeNote, updateNote, notes, activeNotesSource, getNotes } from '@/composables/useNotes'
|
||||||
import { viewModes, activeViewMode } from '@/composables/useViewMode'
|
import { viewModes, activeViewMode } from '@/composables/useViewMode'
|
||||||
import {
|
import {
|
||||||
getClientKey,
|
getClientKey,
|
||||||
getEncryptionKey,
|
getEncryptionKey,
|
||||||
encryptionKey,
|
encryptionKey,
|
||||||
setClientKey,
|
setClientKey,
|
||||||
passphraseRequired
|
passphraseRequired
|
||||||
} from '@/composables/useEncryption'
|
} from '@/composables/useEncryption'
|
||||||
import { initializeSettings } from '@/composables/useSettings'
|
import { initializeSettings } from '@/composables/useSettings'
|
||||||
import { windowIsMobile } from '@/utils/helpers'
|
import { windowIsMobile } from '@/utils/helpers'
|
||||||
import SideBar from '@/components/SideBar.vue'
|
import SideBar from '@/components/SideBar.vue'
|
||||||
import { useWindowSize } from '@vueuse/core'
|
import { useWindowSize } from '@vueuse/core'
|
||||||
import { SplashScreen } from '@capacitor/splash-screen'
|
|
||||||
|
|
||||||
initializeSettings()
|
initializeSettings()
|
||||||
|
|
||||||
onMounted(() => SplashScreen.hide())
|
|
||||||
|
|
||||||
const sideBarCollapsed = ref<boolean>(windowIsMobile())
|
const sideBarCollapsed = ref<boolean>(windowIsMobile())
|
||||||
|
|
||||||
const { width } = useWindowSize()
|
const { width } = useWindowSize()
|
||||||
@@ -32,28 +29,28 @@ watch(width, () => (sideBarCollapsed.value = windowIsMobile()))
|
|||||||
// provide('firebaseAuthUI', firebaseAuthUI)
|
// provide('firebaseAuthUI', firebaseAuthUI)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[activeNotesSource, encryptionKey],
|
[activeNotesSource, encryptionKey],
|
||||||
() => {
|
() => {
|
||||||
if (activeNotesSource.value === 'firebase') {
|
if (activeNotesSource.value === 'firebase') {
|
||||||
getClientKey()
|
getClientKey()
|
||||||
getEncryptionKey()
|
getEncryptionKey()
|
||||||
}
|
}
|
||||||
getNotes()
|
getNotes()
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
const passphrase = ref('')
|
const passphrase = ref('')
|
||||||
const passphraseValid = ref<boolean>()
|
const passphraseValid = ref<boolean>()
|
||||||
|
|
||||||
const submitPassphrase = (close: () => void) => {
|
const submitPassphrase = (close: () => void) => {
|
||||||
const setClientKeyResult = setClientKey(passphrase.value)
|
const setClientKeyResult = setClientKey(passphrase.value)
|
||||||
passphraseValid.value = setClientKeyResult
|
passphraseValid.value = setClientKeyResult
|
||||||
if (passphraseValid.value) close()
|
if (passphraseValid.value) close()
|
||||||
}
|
}
|
||||||
|
|
||||||
const loading = computed(
|
const loading = computed(
|
||||||
() => notes.value.length === 0 || passphraseRequired.value || !activeNotesSource.value
|
() => notes.value.length === 0 || passphraseRequired.value || !activeNotesSource.value
|
||||||
)
|
)
|
||||||
provide('loading', loading)
|
provide('loading', loading)
|
||||||
|
|
||||||
@@ -62,90 +59,90 @@ const topBarHeightWithSafeArea = computed(() => `calc(${topBarHeight}px + var(--
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<TopBar
|
<TopBar
|
||||||
:side-bar-collapsed="sideBarCollapsed"
|
:side-bar-collapsed="sideBarCollapsed"
|
||||||
:height="topBarHeight"
|
:height="topBarHeight"
|
||||||
:style="{ height: topBarHeightWithSafeArea }"
|
:style="{ height: topBarHeightWithSafeArea }"
|
||||||
@toggle-side-bar="sideBarCollapsed = !sideBarCollapsed"
|
@toggle-side-bar="sideBarCollapsed = !sideBarCollapsed"
|
||||||
class="pe-[var(--safe-area-right)] ps-[var(--safe-area-left)]"
|
class="pe-[var(--safe-area-right)] ps-[var(--safe-area-left)]"
|
||||||
/>
|
/>
|
||||||
<!-- <div class="absolute bottom-0 left-0 right-0 top-[50px] mx-auto flex flex-grow"> -->
|
<!-- <div class="absolute bottom-0 left-0 right-0 top-[50px] mx-auto flex flex-grow"> -->
|
||||||
<div
|
<div
|
||||||
class="mx-auto flex w-full max-w-app flex-grow pe-[var(--safe-area-right)] ps-[var(--safe-area-left)]"
|
class="mx-auto flex w-full max-w-app flex-grow pe-[var(--safe-area-right)] ps-[var(--safe-area-left)]"
|
||||||
|
>
|
||||||
|
<Transition name="sidebar">
|
||||||
|
<SideBar
|
||||||
|
:view-modes="viewModes"
|
||||||
|
:active-view-mode="activeViewMode"
|
||||||
|
@set-view-mode="(viewMode) => (activeViewMode = viewMode)"
|
||||||
|
@collapse="(collapse) => (sideBarCollapsed = collapse)"
|
||||||
|
class="bg-gray-100 px-3 py-6 transition-[width] delay-200 duration-0 max-sm:z-50 max-sm:border-x-[1px] max-sm:py-3 max-sm:transition-transform max-sm:delay-0 max-sm:duration-200"
|
||||||
|
:style="{ 'margin-top': topBarHeightWithSafeArea }"
|
||||||
|
v-if="!sideBarCollapsed"
|
||||||
|
/>
|
||||||
|
</Transition>
|
||||||
|
<Transition name="overlay">
|
||||||
|
<div
|
||||||
|
class="absolute bottom-0 left-0 right-0 top-0 z-40 cursor-pointer bg-neutral-800 bg-opacity-60 transition-opacity duration-200 sm:hidden"
|
||||||
|
@click="sideBarCollapsed = true"
|
||||||
|
v-if="!sideBarCollapsed"
|
||||||
|
/>
|
||||||
|
</Transition>
|
||||||
|
<main
|
||||||
|
class="transition[margin-left] z-10 mx-auto flex h-full w-full max-w-app flex-col overflow-y-auto border-x-[1px] bg-white pb-[var(--safe-area-bottom)] duration-200 ease-out"
|
||||||
|
:class="sideBarCollapsed ? 'ml-0' : 'sm:ml-sidebar'"
|
||||||
>
|
>
|
||||||
<Transition name="sidebar">
|
<div class="flex h-full w-full px-10 py-6 max-sm:px-4 max-sm:py-3">
|
||||||
<SideBar
|
<template v-if="!loading">
|
||||||
:view-modes="viewModes"
|
<Note
|
||||||
:active-view-mode="activeViewMode"
|
v-if="activeViewMode.name === 'Note' && activeNote"
|
||||||
@set-view-mode="(viewMode) => (activeViewMode = viewMode)"
|
:key="activeNote.id"
|
||||||
@collapse="(collapse) => (sideBarCollapsed = collapse)"
|
:note="activeNote"
|
||||||
class="bg-gray-100 px-3 py-6 transition-[width] delay-200 duration-0 max-sm:z-50 max-sm:border-x-[1px] max-sm:py-3 max-sm:transition-transform max-sm:delay-0 max-sm:duration-200"
|
class=""
|
||||||
:style="{ 'margin-top': topBarHeightWithSafeArea }"
|
@update="(note) => updateNote(note.id, note)"
|
||||||
v-if="!sideBarCollapsed"
|
/>
|
||||||
/>
|
<ListView v-else-if="activeViewMode.name === 'List'" />
|
||||||
</Transition>
|
<Mindmap v-else-if="activeViewMode.name === 'Mindmap'" />
|
||||||
<Transition name="overlay">
|
|
||||||
<div
|
|
||||||
class="absolute bottom-0 left-0 right-0 top-0 z-40 cursor-pointer bg-neutral-800 bg-opacity-60 transition-opacity duration-200 sm:hidden"
|
|
||||||
@click="sideBarCollapsed = true"
|
|
||||||
v-if="!sideBarCollapsed"
|
|
||||||
/>
|
|
||||||
</Transition>
|
|
||||||
<main
|
|
||||||
class="transition[margin-left] z-10 mx-auto flex h-full w-full max-w-app flex-col overflow-y-auto bg-white pb-[var(--safe-area-bottom)] duration-200 ease-out sm:border-x-[1px]"
|
|
||||||
:class="sideBarCollapsed ? 'ml-0' : 'sm:ml-sidebar'"
|
|
||||||
>
|
|
||||||
<div class="flex w-full flex-grow px-10 py-6 max-sm:px-4 max-sm:py-3">
|
|
||||||
<template v-if="!loading">
|
|
||||||
<Note
|
|
||||||
v-if="activeViewMode.name === 'Note' && activeNote"
|
|
||||||
:key="activeNote.id"
|
|
||||||
:note="activeNote"
|
|
||||||
class=""
|
|
||||||
@update="(note) => updateNote(note.id, note)"
|
|
||||||
/>
|
|
||||||
<ListView v-else-if="activeViewMode.name === 'List'" />
|
|
||||||
<Mindmap v-else-if="activeViewMode.name === 'Mindmap'" />
|
|
||||||
</template>
|
|
||||||
<SkeletonNote v-else />
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
<UIModal :open="passphraseRequired" persistent>
|
|
||||||
<template #title>Enter your passphrase</template>
|
|
||||||
<template #default="{ close }">
|
|
||||||
<div>
|
|
||||||
Your notes are encrypted. Please enter your encryption key passphrase to decrypt
|
|
||||||
your cloud notes.
|
|
||||||
</div>
|
|
||||||
<form @submit.prevent="submitPassphrase(close)">
|
|
||||||
<UIInputText
|
|
||||||
type="password"
|
|
||||||
class="w-full !max-w-full"
|
|
||||||
:color="passphraseValid === false ? 'error' : 'regular'"
|
|
||||||
v-model="passphrase"
|
|
||||||
></UIInputText>
|
|
||||||
</form>
|
|
||||||
<UIAlert color="error" class="mt-4" v-if="passphraseValid === false">
|
|
||||||
<i class="fa-solid fa-triangle-exclamation"></i>
|
|
||||||
The passphrase you entered is incorrect.
|
|
||||||
</UIAlert>
|
|
||||||
</template>
|
</template>
|
||||||
<template #actions="{ close }">
|
<SkeletonNote v-else />
|
||||||
<UIButton color="primary" size="sm" @click="submitPassphrase(close)">Submit</UIButton>
|
</div>
|
||||||
</template>
|
</main>
|
||||||
</UIModal>
|
</div>
|
||||||
|
<UIModal :open="passphraseRequired" :persistent="true">
|
||||||
|
<template #title>Enter your passphrase</template>
|
||||||
|
<template #default="{ close }">
|
||||||
|
<div>
|
||||||
|
Your notes are encrypted. Please enter your encryption key passphrase to decrypt your cloud
|
||||||
|
notes.
|
||||||
|
</div>
|
||||||
|
<form @submit.prevent="submitPassphrase(close)">
|
||||||
|
<UIInputText
|
||||||
|
type="password"
|
||||||
|
class="w-full !max-w-full"
|
||||||
|
:color="passphraseValid === false ? 'error' : 'regular'"
|
||||||
|
v-model="passphrase"
|
||||||
|
></UIInputText>
|
||||||
|
</form>
|
||||||
|
<UIAlert color="error" class="mt-4" v-if="passphraseValid === false">
|
||||||
|
<i class="fa-solid fa-triangle-exclamation"></i>
|
||||||
|
The passphrase you entered is incorrect.
|
||||||
|
</UIAlert>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ close }">
|
||||||
|
<UIButton color="primary" size="sm" @click="submitPassphrase(close)">Submit</UIButton>
|
||||||
|
</template>
|
||||||
|
</UIModal>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.sidebar-enter-from,
|
.sidebar-enter-from,
|
||||||
.sidebar-leave-to {
|
.sidebar-leave-to {
|
||||||
@apply max-sm:-translate-x-full;
|
@apply max-sm:-translate-x-full;
|
||||||
}
|
}
|
||||||
.overlay-enter-from,
|
.overlay-enter-from,
|
||||||
.overlay-leave-to {
|
.overlay-leave-to {
|
||||||
@apply opacity-0;
|
@apply opacity-0;
|
||||||
}
|
}
|
||||||
main {
|
main {
|
||||||
contain: size layout style;
|
contain: size layout style;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 59 KiB |
|
Before Width: | Height: | Size: 65 KiB |
|
Before Width: | Height: | Size: 50 KiB |
@@ -15,228 +15,226 @@ const SAMPLE_READ_ONLY_LOCK_ID = 'Integration Sample'
|
|||||||
const INPUT_EVENT_DEBOUNCE_WAIT = 300
|
const INPUT_EVENT_DEBOUNCE_WAIT = 300
|
||||||
|
|
||||||
export interface CKEditorComponentData {
|
export interface CKEditorComponentData {
|
||||||
instance: Editor | null
|
instance: Editor | null
|
||||||
lastEditorData: string | null
|
lastEditorData: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'Ckeditor',
|
name: 'Ckeditor',
|
||||||
|
|
||||||
model: {
|
model: {
|
||||||
prop: 'modelValue',
|
prop: 'modelValue',
|
||||||
event: 'update:modelValue'
|
event: 'update:modelValue'
|
||||||
|
},
|
||||||
|
|
||||||
|
props: {
|
||||||
|
editor: {
|
||||||
|
type: Function as unknown as PropType<{
|
||||||
|
create(...args: any): Promise<Editor>
|
||||||
|
}>,
|
||||||
|
required: true
|
||||||
},
|
},
|
||||||
|
config: {
|
||||||
props: {
|
type: Object as PropType<EditorConfig>,
|
||||||
editor: {
|
default: () => ({})
|
||||||
type: Function as unknown as PropType<{
|
|
||||||
create(...args: any): Promise<Editor>
|
|
||||||
}>,
|
|
||||||
required: true
|
|
||||||
},
|
|
||||||
config: {
|
|
||||||
type: Object as PropType<EditorConfig>,
|
|
||||||
default: () => ({})
|
|
||||||
},
|
|
||||||
modelValue: {
|
|
||||||
type: String,
|
|
||||||
default: ''
|
|
||||||
},
|
|
||||||
tagName: {
|
|
||||||
type: String,
|
|
||||||
default: 'div'
|
|
||||||
},
|
|
||||||
disabled: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
},
|
|
||||||
disableTwoWayDataBinding: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
modelValue: {
|
||||||
emits: [
|
type: String,
|
||||||
'ready',
|
default: ''
|
||||||
'destroy',
|
|
||||||
'blur',
|
|
||||||
'focus',
|
|
||||||
'input',
|
|
||||||
'update:modelValue',
|
|
||||||
'click',
|
|
||||||
'editorReady',
|
|
||||||
'contextedLinkAutocomplete',
|
|
||||||
'contextedKeypress'
|
|
||||||
],
|
|
||||||
|
|
||||||
data(): CKEditorComponentData {
|
|
||||||
return {
|
|
||||||
// Don't define it in #props because it produces a warning.
|
|
||||||
// https://v3.vuejs.org/guide/component-props.html#one-way-data-flow
|
|
||||||
instance: null,
|
|
||||||
lastEditorData: null
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
tagName: {
|
||||||
watch: {
|
type: String,
|
||||||
modelValue(value) {
|
default: 'div'
|
||||||
// Synchronize changes of #modelValue. There are two sources of changes:
|
|
||||||
//
|
|
||||||
// External modelValue change ──────╮
|
|
||||||
// ╰─────> ┏━━━━━━━━━━━┓
|
|
||||||
// ┃ Component ┃
|
|
||||||
// ╭─────> ┗━━━━━━━━━━━┛
|
|
||||||
// Internal data change ──────╯
|
|
||||||
// (typing, commands, collaboration)
|
|
||||||
//
|
|
||||||
// Case 1: If the change was external (via props), the editor data must be synced with
|
|
||||||
// the component using instance#setData() and it is OK to destroy the selection.
|
|
||||||
//
|
|
||||||
// Case 2: If the change is the result of internal data change, the #modelValue is the
|
|
||||||
// same as this.lastEditorData, which has been cached on #change:data. If we called
|
|
||||||
// instance#setData() at this point, that would demolish the selection.
|
|
||||||
//
|
|
||||||
// To limit the number of instance#setData() which is time-consuming when there is a
|
|
||||||
// lot of data we make sure:
|
|
||||||
// * the new modelValue is at least different than the old modelValue (Case 1.)
|
|
||||||
// * the new modelValue is different than the last internal instance state (Case 2.)
|
|
||||||
//
|
|
||||||
// See: https://github.com/ckeditor/ckeditor5-vue/issues/42.
|
|
||||||
if (this.instance && value !== this.lastEditorData) {
|
|
||||||
this.instance.data.set(value)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// Synchronize changes of #disabled.
|
|
||||||
disabled(readOnlyMode) {
|
|
||||||
if (readOnlyMode) {
|
|
||||||
this.instance!.enableReadOnlyMode(SAMPLE_READ_ONLY_LOCK_ID)
|
|
||||||
} else {
|
|
||||||
this.instance!.disableReadOnlyMode(SAMPLE_READ_ONLY_LOCK_ID)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
disabled: {
|
||||||
created() {
|
type: Boolean,
|
||||||
const { CKEDITOR_VERSION } = window
|
default: false
|
||||||
|
|
||||||
if (CKEDITOR_VERSION) {
|
|
||||||
const [major] = CKEDITOR_VERSION.split('.').map(Number)
|
|
||||||
|
|
||||||
if (major < 37) {
|
|
||||||
console.warn(
|
|
||||||
'The <CKEditor> component requires using CKEditor 5 in version 37 or higher.'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.warn('Cannot find the "CKEDITOR_VERSION" in the "window" scope.')
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
|
disableTwoWayDataBinding: {
|
||||||
mounted() {
|
type: Boolean,
|
||||||
// Clone the config first so it never gets mutated (across multiple editor instances).
|
default: false
|
||||||
// https://github.com/ckeditor/ckeditor5-vue/issues/101
|
|
||||||
const editorConfig: EditorConfig = Object.assign({}, this.config)
|
|
||||||
|
|
||||||
if (this.modelValue) {
|
|
||||||
editorConfig.initialData = this.modelValue
|
|
||||||
}
|
|
||||||
|
|
||||||
this.editor
|
|
||||||
.create(this.$el, editorConfig)
|
|
||||||
.then((editor) => {
|
|
||||||
// Save the reference to the instance for further use.
|
|
||||||
this.instance = markRaw(editor)
|
|
||||||
|
|
||||||
this.setUpEditorEvents()
|
|
||||||
|
|
||||||
// Synchronize the editor content. The #modelValue may change while the editor is being created, so the editor content has
|
|
||||||
// to be synchronized with these potential changes as soon as it is ready.
|
|
||||||
if (this.modelValue !== editorConfig.initialData) {
|
|
||||||
editor.data.set(this.modelValue)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set initial disabled state.
|
|
||||||
if (this.disabled) {
|
|
||||||
editor.enableReadOnlyMode(SAMPLE_READ_ONLY_LOCK_ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Let the world know the editor is ready.
|
|
||||||
this.$emit('ready', editor)
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error(error)
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
beforeUnmount() {
|
|
||||||
if (this.instance) {
|
|
||||||
this.instance.destroy()
|
|
||||||
this.instance = null
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: By the time the editor is destroyed (promise resolved, editor#destroy fired)
|
|
||||||
// the Vue component will not be able to emit any longer. So emitting #destroy a bit earlier.
|
|
||||||
this.$emit('destroy', this.instance)
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
setUpEditorEvents() {
|
|
||||||
const editor = this.instance!
|
|
||||||
this.$emit('editorReady', editor)
|
|
||||||
|
|
||||||
// Use the leading edge so the first event in the series is emitted immediately.
|
|
||||||
// Failing to do so leads to race conditions, for instance, when the component modelValue
|
|
||||||
// is set twice in a time span shorter than the debounce time.
|
|
||||||
// See https://github.com/ckeditor/ckeditor5-vue/issues/149.
|
|
||||||
const emitDebouncedInputEvent = debounce(
|
|
||||||
(evt) => {
|
|
||||||
if (this.disableTwoWayDataBinding) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cache the last editor data. This kind of data is a result of typing,
|
|
||||||
// editor command execution, collaborative changes to the document, etc.
|
|
||||||
// This data is compared when the component modelValue changes in a 2-way binding.
|
|
||||||
const data = (this.lastEditorData = editor.data.get())
|
|
||||||
|
|
||||||
// The compatibility with the v-model and general Vue.js concept of input–like components.
|
|
||||||
this.$emit('update:modelValue', data, evt, editor)
|
|
||||||
this.$emit('input', data, evt, editor)
|
|
||||||
},
|
|
||||||
INPUT_EVENT_DEBOUNCE_WAIT,
|
|
||||||
{ leading: true }
|
|
||||||
)
|
|
||||||
|
|
||||||
// Debounce emitting the #input event. When data is huge, instance#getData()
|
|
||||||
// takes a lot of time to execute on every single key press and ruins the UX.
|
|
||||||
//
|
|
||||||
// See: https://github.com/ckeditor/ckeditor5-vue/issues/42
|
|
||||||
editor.model.document.on('change:data', emitDebouncedInputEvent)
|
|
||||||
|
|
||||||
editor.editing.view.document.on('focus', (evt) => {
|
|
||||||
this.$emit('focus', evt, editor)
|
|
||||||
})
|
|
||||||
|
|
||||||
editor.editing.view.document.on('blur', (evt) => {
|
|
||||||
this.$emit('blur', evt, editor)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Custom event
|
|
||||||
editor.editing.view.document.on('click', (evt, data) => {
|
|
||||||
this.$emit('click', { evt, data }, editor)
|
|
||||||
})
|
|
||||||
|
|
||||||
editor.model.document.on('contextedLinkAutocomplete', (_, data) => {
|
|
||||||
this.$emit('contextedLinkAutocomplete', data)
|
|
||||||
})
|
|
||||||
editor.model.document.on('contextedKeypress', (_, eventData) => {
|
|
||||||
this.$emit('contextedKeypress', eventData)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
|
||||||
return h(this.tagName)
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
emits: [
|
||||||
|
'ready',
|
||||||
|
'destroy',
|
||||||
|
'blur',
|
||||||
|
'focus',
|
||||||
|
'input',
|
||||||
|
'update:modelValue',
|
||||||
|
'click',
|
||||||
|
'editorReady',
|
||||||
|
'contextedLinkAutocomplete',
|
||||||
|
'contextedKeypress'
|
||||||
|
],
|
||||||
|
|
||||||
|
data(): CKEditorComponentData {
|
||||||
|
return {
|
||||||
|
// Don't define it in #props because it produces a warning.
|
||||||
|
// https://v3.vuejs.org/guide/component-props.html#one-way-data-flow
|
||||||
|
instance: null,
|
||||||
|
lastEditorData: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
modelValue(value) {
|
||||||
|
// Synchronize changes of #modelValue. There are two sources of changes:
|
||||||
|
//
|
||||||
|
// External modelValue change ──────╮
|
||||||
|
// ╰─────> ┏━━━━━━━━━━━┓
|
||||||
|
// ┃ Component ┃
|
||||||
|
// ╭─────> ┗━━━━━━━━━━━┛
|
||||||
|
// Internal data change ──────╯
|
||||||
|
// (typing, commands, collaboration)
|
||||||
|
//
|
||||||
|
// Case 1: If the change was external (via props), the editor data must be synced with
|
||||||
|
// the component using instance#setData() and it is OK to destroy the selection.
|
||||||
|
//
|
||||||
|
// Case 2: If the change is the result of internal data change, the #modelValue is the
|
||||||
|
// same as this.lastEditorData, which has been cached on #change:data. If we called
|
||||||
|
// instance#setData() at this point, that would demolish the selection.
|
||||||
|
//
|
||||||
|
// To limit the number of instance#setData() which is time-consuming when there is a
|
||||||
|
// lot of data we make sure:
|
||||||
|
// * the new modelValue is at least different than the old modelValue (Case 1.)
|
||||||
|
// * the new modelValue is different than the last internal instance state (Case 2.)
|
||||||
|
//
|
||||||
|
// See: https://github.com/ckeditor/ckeditor5-vue/issues/42.
|
||||||
|
if (this.instance && value !== this.lastEditorData) {
|
||||||
|
this.instance.data.set(value)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// Synchronize changes of #disabled.
|
||||||
|
disabled(readOnlyMode) {
|
||||||
|
if (readOnlyMode) {
|
||||||
|
this.instance!.enableReadOnlyMode(SAMPLE_READ_ONLY_LOCK_ID)
|
||||||
|
} else {
|
||||||
|
this.instance!.disableReadOnlyMode(SAMPLE_READ_ONLY_LOCK_ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
created() {
|
||||||
|
const { CKEDITOR_VERSION } = window
|
||||||
|
|
||||||
|
if (CKEDITOR_VERSION) {
|
||||||
|
const [major] = CKEDITOR_VERSION.split('.').map(Number)
|
||||||
|
|
||||||
|
if (major < 37) {
|
||||||
|
console.warn('The <CKEditor> component requires using CKEditor 5 in version 37 or higher.')
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.warn('Cannot find the "CKEDITOR_VERSION" in the "window" scope.')
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
// Clone the config first so it never gets mutated (across multiple editor instances).
|
||||||
|
// https://github.com/ckeditor/ckeditor5-vue/issues/101
|
||||||
|
const editorConfig: EditorConfig = Object.assign({}, this.config)
|
||||||
|
|
||||||
|
if (this.modelValue) {
|
||||||
|
editorConfig.initialData = this.modelValue
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editor
|
||||||
|
.create(this.$el, editorConfig)
|
||||||
|
.then((editor) => {
|
||||||
|
// Save the reference to the instance for further use.
|
||||||
|
this.instance = markRaw(editor)
|
||||||
|
|
||||||
|
this.setUpEditorEvents()
|
||||||
|
|
||||||
|
// Synchronize the editor content. The #modelValue may change while the editor is being created, so the editor content has
|
||||||
|
// to be synchronized with these potential changes as soon as it is ready.
|
||||||
|
if (this.modelValue !== editorConfig.initialData) {
|
||||||
|
editor.data.set(this.modelValue)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set initial disabled state.
|
||||||
|
if (this.disabled) {
|
||||||
|
editor.enableReadOnlyMode(SAMPLE_READ_ONLY_LOCK_ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Let the world know the editor is ready.
|
||||||
|
this.$emit('ready', editor)
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error(error)
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeUnmount() {
|
||||||
|
if (this.instance) {
|
||||||
|
this.instance.destroy()
|
||||||
|
this.instance = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: By the time the editor is destroyed (promise resolved, editor#destroy fired)
|
||||||
|
// the Vue component will not be able to emit any longer. So emitting #destroy a bit earlier.
|
||||||
|
this.$emit('destroy', this.instance)
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
setUpEditorEvents() {
|
||||||
|
const editor = this.instance!
|
||||||
|
this.$emit('editorReady', editor)
|
||||||
|
|
||||||
|
// Use the leading edge so the first event in the series is emitted immediately.
|
||||||
|
// Failing to do so leads to race conditions, for instance, when the component modelValue
|
||||||
|
// is set twice in a time span shorter than the debounce time.
|
||||||
|
// See https://github.com/ckeditor/ckeditor5-vue/issues/149.
|
||||||
|
const emitDebouncedInputEvent = debounce(
|
||||||
|
(evt) => {
|
||||||
|
if (this.disableTwoWayDataBinding) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache the last editor data. This kind of data is a result of typing,
|
||||||
|
// editor command execution, collaborative changes to the document, etc.
|
||||||
|
// This data is compared when the component modelValue changes in a 2-way binding.
|
||||||
|
const data = (this.lastEditorData = editor.data.get())
|
||||||
|
|
||||||
|
// The compatibility with the v-model and general Vue.js concept of input–like components.
|
||||||
|
this.$emit('update:modelValue', data, evt, editor)
|
||||||
|
this.$emit('input', data, evt, editor)
|
||||||
|
},
|
||||||
|
INPUT_EVENT_DEBOUNCE_WAIT,
|
||||||
|
{ leading: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// Debounce emitting the #input event. When data is huge, instance#getData()
|
||||||
|
// takes a lot of time to execute on every single key press and ruins the UX.
|
||||||
|
//
|
||||||
|
// See: https://github.com/ckeditor/ckeditor5-vue/issues/42
|
||||||
|
editor.model.document.on('change:data', emitDebouncedInputEvent)
|
||||||
|
|
||||||
|
editor.editing.view.document.on('focus', (evt) => {
|
||||||
|
this.$emit('focus', evt, editor)
|
||||||
|
})
|
||||||
|
|
||||||
|
editor.editing.view.document.on('blur', (evt) => {
|
||||||
|
this.$emit('blur', evt, editor)
|
||||||
|
})
|
||||||
|
|
||||||
|
// Custom event
|
||||||
|
editor.editing.view.document.on('click', (evt, data) => {
|
||||||
|
this.$emit('click', { evt, data }, editor)
|
||||||
|
})
|
||||||
|
|
||||||
|
editor.model.document.on('contextedLinkAutocomplete', (_, data) => {
|
||||||
|
this.$emit('contextedLinkAutocomplete', data)
|
||||||
|
})
|
||||||
|
editor.model.document.on('contextedKeypress', (_, eventData) => {
|
||||||
|
this.$emit('contextedKeypress', eventData)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return h(this.tagName)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -6,184 +6,178 @@ import Rect from '@ckeditor/ckeditor5-utils/src/dom/rect'
|
|||||||
const HIGHLIGHT_CLASS = 'ck-link_selected'
|
const HIGHLIGHT_CLASS = 'ck-link_selected'
|
||||||
|
|
||||||
export default class ContextedLinkEditing extends Plugin {
|
export default class ContextedLinkEditing extends Plugin {
|
||||||
init() {
|
init() {
|
||||||
this._defineSchema() // ADDED
|
this._defineSchema() // ADDED
|
||||||
this._defineConverters() // ADDED
|
this._defineConverters() // ADDED
|
||||||
this._addContextedKeyHandler()
|
this._addContextedKeyHandler()
|
||||||
const twoStepCaretMovementPlugin = this.editor.plugins.get(TwoStepCaretMovement)
|
const twoStepCaretMovementPlugin = this.editor.plugins.get(TwoStepCaretMovement)
|
||||||
twoStepCaretMovementPlugin.registerAttribute('contextedLink')
|
twoStepCaretMovementPlugin.registerAttribute('contextedLink')
|
||||||
inlineHighlight(this.editor, 'contextedLink', 'a', HIGHLIGHT_CLASS)
|
inlineHighlight(this.editor, 'contextedLink', 'a', HIGHLIGHT_CLASS)
|
||||||
this.editor.commands.add('autocomplete', new AttributeCommand(this.editor, 'autocomplete'))
|
this.editor.commands.add('autocomplete', new AttributeCommand(this.editor, 'autocomplete'))
|
||||||
}
|
}
|
||||||
afterInit() {
|
afterInit() {
|
||||||
this._addAutocomplete()
|
this._addAutocomplete()
|
||||||
}
|
}
|
||||||
_defineSchema() {
|
_defineSchema() {
|
||||||
// ADDED
|
// ADDED
|
||||||
const schema = this.editor.model.schema
|
const schema = this.editor.model.schema
|
||||||
|
|
||||||
// Extend the text node's schema to accept the abbreviation attribute.
|
// Extend the text node's schema to accept the abbreviation attribute.
|
||||||
schema.extend('$text', {
|
schema.extend('$text', {
|
||||||
allowAttributes: ['contextedLink', 'autocomplete']
|
allowAttributes: ['contextedLink', 'autocomplete']
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_defineConverters() {
|
||||||
|
// ADDED
|
||||||
|
const conversion = this.editor.conversion
|
||||||
|
|
||||||
|
// Conversion from a model attribute to a view element.
|
||||||
|
conversion.for('downcast').attributeToElement({
|
||||||
|
model: 'contextedLink',
|
||||||
|
// Callback function provides access to the model attribute value
|
||||||
|
// and the DowncastWriter.
|
||||||
|
view: (modelAttributeValue, conversionApi) => {
|
||||||
|
const { writer } = conversionApi
|
||||||
|
|
||||||
|
return writer.createAttributeElement('a', {
|
||||||
|
'data-contexted-link': modelAttributeValue
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
_defineConverters() {
|
})
|
||||||
// ADDED
|
conversion.for('upcast').elementToAttribute({
|
||||||
const conversion = this.editor.conversion
|
view: {
|
||||||
|
name: 'a',
|
||||||
|
key: 'data-contexted-link'
|
||||||
|
},
|
||||||
|
model: {
|
||||||
|
key: 'contextedLink'
|
||||||
|
},
|
||||||
|
converterPriority: 'high'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_addAutocomplete() {
|
||||||
|
// Copied from: node_modules/@ckeditor/ckeditor5-autoformat/src/inlineautoformatediting.js
|
||||||
|
const editor = this.editor
|
||||||
|
let showAutocomplete = false
|
||||||
|
editor.model.document.on('change', (_, batch) => {
|
||||||
|
if (batch.isUndo || !batch.isLocal) return
|
||||||
|
const model = editor.model
|
||||||
|
const selection = model.document.selection
|
||||||
|
// Do nothing if selection is not collapsed.
|
||||||
|
if (!selection.isCollapsed) return
|
||||||
|
const changes = Array.from(model.document.differ.getChanges())
|
||||||
|
const entry = changes[0]
|
||||||
|
// Typing is represented by only a single change.
|
||||||
|
if (
|
||||||
|
changes.length != 1 ||
|
||||||
|
(entry.type !== 'insert' && entry.type !== 'remove') ||
|
||||||
|
(entry.name != '$text' && entry.name != 'paragraph') ||
|
||||||
|
entry.length != 1
|
||||||
|
) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Conversion from a model attribute to a view element.
|
const focus = selection.focus
|
||||||
conversion.for('downcast').attributeToElement({
|
const block = focus?.parent
|
||||||
model: 'contextedLink',
|
if (!block || !focus) return
|
||||||
// Callback function provides access to the model attribute value
|
const { text, range } = getTextAfterCode(
|
||||||
// and the DowncastWriter.
|
model.createRange(model.createPositionAt(block, 0), focus),
|
||||||
view: (modelAttributeValue, conversionApi) => {
|
model
|
||||||
const { writer } = conversionApi
|
)
|
||||||
|
const inputText = (text as string).split(']]').slice(-1)[0]
|
||||||
|
const autocompleteText = (inputText as string).match(/(?<=\[\[).*/g)
|
||||||
|
const cursorNodes = [focus.textNode, focus.nodeBefore, focus.nodeAfter]
|
||||||
|
const autocompleteNode: any = cursorNodes.find((node) =>
|
||||||
|
['contextedLink', 'autocomplete'].some((attribute) => node?.hasAttribute(attribute))
|
||||||
|
)
|
||||||
|
|
||||||
return writer.createAttributeElement('a', {
|
if (Boolean(autocompleteText) !== Boolean(autocompleteNode)) {
|
||||||
'data-contexted-link': modelAttributeValue
|
editor.execute('autocomplete')
|
||||||
})
|
}
|
||||||
}
|
const autocompleteActive = ['contextedLink', 'autocomplete'].some((attribute) => {
|
||||||
})
|
return editor.model.document.selection.hasAttribute(attribute)
|
||||||
conversion.for('upcast').elementToAttribute({
|
})
|
||||||
view: {
|
showAutocomplete = autocompleteActive
|
||||||
name: 'a',
|
fireAutocompleteEvent(editor, showAutocomplete, autocompleteNode)
|
||||||
key: 'data-contexted-link'
|
|
||||||
},
|
|
||||||
model: {
|
|
||||||
key: 'contextedLink'
|
|
||||||
},
|
|
||||||
converterPriority: 'high'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_addAutocomplete() {
|
|
||||||
// Copied from: node_modules/@ckeditor/ckeditor5-autoformat/src/inlineautoformatediting.js
|
|
||||||
const editor = this.editor
|
|
||||||
let showAutocomplete = false
|
|
||||||
editor.model.document.on('change', (_, batch) => {
|
|
||||||
if (batch.isUndo || !batch.isLocal) return
|
|
||||||
const model = editor.model
|
|
||||||
const selection = model.document.selection
|
|
||||||
// Do nothing if selection is not collapsed.
|
|
||||||
if (!selection.isCollapsed) return
|
|
||||||
const changes = Array.from(model.document.differ.getChanges())
|
|
||||||
const entry = changes[0]
|
|
||||||
// Typing is represented by only a single change.
|
|
||||||
if (
|
|
||||||
changes.length != 1 ||
|
|
||||||
(entry.type !== 'insert' && entry.type !== 'remove') ||
|
|
||||||
(entry.name != '$text' && entry.name != 'paragraph') ||
|
|
||||||
entry.length != 1
|
|
||||||
) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const focus = selection.focus
|
const regexFormat = /(\[\[)([^[]+?)(\]\])$/g
|
||||||
const block = focus?.parent
|
let result
|
||||||
if (!block || !focus) return
|
const format: Array<number>[] = []
|
||||||
const { text, range } = getTextAfterCode(
|
while ((result = regexFormat.exec(text as string)) !== null) {
|
||||||
model.createRange(model.createPositionAt(block, 0), focus),
|
if (result && result.length < 4) {
|
||||||
model
|
break
|
||||||
)
|
}
|
||||||
const inputText = (text as string).split(']]').slice(-1)[0]
|
let index = result.index
|
||||||
const autocompleteText = (inputText as string).match(/(?<=\[\[).*/g)
|
const { '1': leftDel, '2': content, '3': rightDel } = result
|
||||||
const cursorNodes = [focus.textNode, focus.nodeBefore, focus.nodeAfter]
|
// Real matched string - there might be some non-capturing groups so we need to recalculate starting index.
|
||||||
const autocompleteNode: any = cursorNodes.find((node) =>
|
const found = leftDel + content + rightDel
|
||||||
['contextedLink', 'autocomplete'].some((attribute) => node?.hasAttribute(attribute))
|
index += result[0].length - found.length
|
||||||
)
|
format.push([index + leftDel.length, index + leftDel.length + content.length])
|
||||||
|
}
|
||||||
if (Boolean(autocompleteText) !== Boolean(autocompleteNode)) {
|
model.enqueueChange((writer) => {
|
||||||
editor.execute('autocomplete')
|
const rangesToFormat = format.map((array) =>
|
||||||
}
|
model.createRange(range.start.getShiftedBy(array[0]), range.start.getShiftedBy(array[1]))
|
||||||
const autocompleteActive = ['contextedLink', 'autocomplete'].some((attribute) => {
|
|
||||||
return editor.model.document.selection.hasAttribute(attribute)
|
|
||||||
})
|
|
||||||
showAutocomplete = autocompleteActive
|
|
||||||
fireAutocompleteEvent(editor, showAutocomplete, autocompleteNode)
|
|
||||||
|
|
||||||
const regexFormat = /(\[\[)([^[]+?)(\]\])$/g
|
|
||||||
let result
|
|
||||||
const format: Array<number>[] = []
|
|
||||||
while ((result = regexFormat.exec(text as string)) !== null) {
|
|
||||||
if (result && result.length < 4) {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
let index = result.index
|
|
||||||
const { '1': leftDel, '2': content, '3': rightDel } = result
|
|
||||||
// Real matched string - there might be some non-capturing groups so we need to recalculate starting index.
|
|
||||||
const found = leftDel + content + rightDel
|
|
||||||
index += result[0].length - found.length
|
|
||||||
format.push([index + leftDel.length, index + leftDel.length + content.length])
|
|
||||||
}
|
|
||||||
model.enqueueChange((writer) => {
|
|
||||||
const rangesToFormat = format.map((array) =>
|
|
||||||
model.createRange(
|
|
||||||
range.start.getShiftedBy(array[0]),
|
|
||||||
range.start.getShiftedBy(array[1])
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
const validRanges = editor.model.schema.getValidRanges(
|
|
||||||
rangesToFormat,
|
|
||||||
'contextedLink'
|
|
||||||
)
|
|
||||||
for (const range of validRanges) {
|
|
||||||
for (const item of range.getItems()) {
|
|
||||||
if ((item as any).data) {
|
|
||||||
writer.setAttribute('contextedLink', true, range)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
_addContextedKeyHandler() {
|
|
||||||
const editor = this.editor
|
|
||||||
const viewDocument = editor.editing.view.document
|
|
||||||
viewDocument.on(
|
|
||||||
'keydown',
|
|
||||||
(evt, data) => {
|
|
||||||
const { keyCode } = data
|
|
||||||
const keyCodesCycle = [38, 40] // Up, Down
|
|
||||||
const keyCodesConfirm = [13] // Enter
|
|
||||||
const keyCodesCancel = [27] // Escape
|
|
||||||
if (keyCodesCancel.includes(keyCode)) {
|
|
||||||
fireAutocompleteEvent(editor, false)
|
|
||||||
}
|
|
||||||
const keyCodes = [...keyCodesConfirm, ...keyCodesCycle]
|
|
||||||
const selection = editor.model.document.selection
|
|
||||||
const selectionInContextedLink = ['contextedLink', 'autocomplete'].some(
|
|
||||||
(attribute) => selection.hasAttribute(attribute)
|
|
||||||
)
|
|
||||||
if (selectionInContextedLink && keyCodes.includes(keyCode)) {
|
|
||||||
if (selection.hasAttribute('contextedLink')) {
|
|
||||||
const autocompleteNode = [
|
|
||||||
selection.focus?.nodeBefore,
|
|
||||||
selection.focus?.textNode,
|
|
||||||
selection.focus?.nodeAfter
|
|
||||||
].find((node) => Boolean(node))
|
|
||||||
fireAutocompleteEvent(editor, true, autocompleteNode)
|
|
||||||
}
|
|
||||||
this.editor.model.document.fire('contextedKeypress', { keyCode })
|
|
||||||
data.preventDefault()
|
|
||||||
evt.stop()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ priority: 'highest' }
|
|
||||||
)
|
)
|
||||||
}
|
|
||||||
|
const validRanges = editor.model.schema.getValidRanges(rangesToFormat, 'contextedLink')
|
||||||
|
for (const range of validRanges) {
|
||||||
|
for (const item of range.getItems()) {
|
||||||
|
if ((item as any).data) {
|
||||||
|
writer.setAttribute('contextedLink', true, range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_addContextedKeyHandler() {
|
||||||
|
const editor = this.editor
|
||||||
|
const viewDocument = editor.editing.view.document
|
||||||
|
viewDocument.on(
|
||||||
|
'keydown',
|
||||||
|
(evt, data) => {
|
||||||
|
const { keyCode } = data
|
||||||
|
const keyCodesCycle = [38, 40] // Up, Down
|
||||||
|
const keyCodesConfirm = [13] // Enter
|
||||||
|
const keyCodesCancel = [27] // Escape
|
||||||
|
if (keyCodesCancel.includes(keyCode)) {
|
||||||
|
fireAutocompleteEvent(editor, false)
|
||||||
|
}
|
||||||
|
const keyCodes = [...keyCodesConfirm, ...keyCodesCycle]
|
||||||
|
const selection = editor.model.document.selection
|
||||||
|
const selectionInContextedLink = ['contextedLink', 'autocomplete'].some((attribute) =>
|
||||||
|
selection.hasAttribute(attribute)
|
||||||
|
)
|
||||||
|
if (selectionInContextedLink && keyCodes.includes(keyCode)) {
|
||||||
|
if (selection.hasAttribute('contextedLink')) {
|
||||||
|
const autocompleteNode = [
|
||||||
|
selection.focus?.nodeBefore,
|
||||||
|
selection.focus?.textNode,
|
||||||
|
selection.focus?.nodeAfter
|
||||||
|
].find((node) => Boolean(node))
|
||||||
|
fireAutocompleteEvent(editor, true, autocompleteNode)
|
||||||
|
}
|
||||||
|
this.editor.model.document.fire('contextedKeypress', { keyCode })
|
||||||
|
data.preventDefault()
|
||||||
|
evt.stop()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ priority: 'highest' }
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNodePosition(editor: any, modelPosition: any) {
|
function getNodePosition(editor: any, modelPosition: any) {
|
||||||
try {
|
try {
|
||||||
const mapper = editor.editing.mapper
|
const mapper = editor.editing.mapper
|
||||||
const viewPosition = mapper.toViewPosition(modelPosition)
|
const viewPosition = mapper.toViewPosition(modelPosition)
|
||||||
const viewRange = editor.editing.view.createRange(viewPosition)
|
const viewRange = editor.editing.view.createRange(viewPosition)
|
||||||
const domConverter = editor.editing.view.domConverter
|
const domConverter = editor.editing.view.domConverter
|
||||||
const rangeRects = Rect.getDomRangeRects(domConverter.viewRangeToDom(viewRange)).pop()
|
const rangeRects = Rect.getDomRangeRects(domConverter.viewRangeToDom(viewRange)).pop()
|
||||||
return rangeRects
|
return rangeRects
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// function testOutputToRanges(start: any, arrays: any[], model: any) {
|
// function testOutputToRanges(start: any, arrays: any[], model: any) {
|
||||||
@@ -198,43 +192,40 @@ function getNodePosition(editor: any, modelPosition: any) {
|
|||||||
// }
|
// }
|
||||||
|
|
||||||
function getTextAfterCode(range: any, model: any) {
|
function getTextAfterCode(range: any, model: any) {
|
||||||
let start = range.start
|
let start = range.start
|
||||||
const text = Array.from(range.getItems()).reduce((rangeText: any, node: any) => {
|
const text = Array.from(range.getItems()).reduce((rangeText: any, node: any) => {
|
||||||
// Trim text to a last occurrence of an inline element and update range start.
|
// Trim text to a last occurrence of an inline element and update range start.
|
||||||
if (!(node.is('$text') || node.is('$textProxy')) || node.getAttribute('code')) {
|
if (!(node.is('$text') || node.is('$textProxy')) || node.getAttribute('code')) {
|
||||||
start = model.createPositionAfter(node)
|
start = model.createPositionAfter(node)
|
||||||
return ''
|
return ''
|
||||||
}
|
}
|
||||||
return rangeText + node.data
|
return rangeText + node.data
|
||||||
}, '')
|
}, '')
|
||||||
return { text, range: model.createRange(start, range.end) }
|
return { text, range: model.createRange(start, range.end) }
|
||||||
}
|
}
|
||||||
|
|
||||||
function fireAutocompleteEvent(editor: any, show: boolean, autocompleteNode?: any) {
|
function fireAutocompleteEvent(editor: any, show: boolean, autocompleteNode?: any) {
|
||||||
let event: AutocompleteEvent
|
let event: AutocompleteEvent
|
||||||
if (show && autocompleteNode) {
|
if (show && autocompleteNode) {
|
||||||
const view = editor.editing.view
|
const view = editor.editing.view
|
||||||
const viewPosition = view.document.selection.focus
|
const viewPosition = view.document.selection.focus
|
||||||
const viewNode = viewPosition?.parent.parent
|
const viewNode = viewPosition?.parent.parent
|
||||||
const domElement = viewNode
|
const domElement = viewNode
|
||||||
? (view.domConverter.mapViewToDom(viewNode) as HTMLElement)
|
? (view.domConverter.mapViewToDom(viewNode) as HTMLElement)
|
||||||
: undefined
|
: undefined
|
||||||
event = {
|
event = {
|
||||||
position: getNodePosition(
|
position: getNodePosition(
|
||||||
editor,
|
editor,
|
||||||
editor.model.createPositionFromPath(
|
editor.model.createPositionFromPath(autocompleteNode.root, autocompleteNode.getPath())
|
||||||
autocompleteNode.root,
|
),
|
||||||
autocompleteNode.getPath()
|
autocompleteText: autocompleteNode.data,
|
||||||
)
|
domElement,
|
||||||
),
|
show: true
|
||||||
autocompleteText: autocompleteNode.data,
|
|
||||||
domElement,
|
|
||||||
show: true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
event = {
|
|
||||||
show: false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
editor.model.document.fire('contextedLinkAutocomplete', event)
|
} else {
|
||||||
|
event = {
|
||||||
|
show: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editor.model.document.fire('contextedLinkAutocomplete', event)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,11 +3,18 @@ import firebase from 'firebase/compat/app'
|
|||||||
import 'firebase/compat/auth'
|
import 'firebase/compat/auth'
|
||||||
import 'firebaseui/dist/firebaseui.css'
|
import 'firebaseui/dist/firebaseui.css'
|
||||||
import * as firebaseui from 'firebaseui'
|
import * as firebaseui from 'firebaseui'
|
||||||
import { FirebaseAuthentication } from '@capacitor-firebase/authentication'
|
import {
|
||||||
import { getAuth, GoogleAuthProvider, signInWithCredential } from 'firebase/auth'
|
FirebaseAuthentication,
|
||||||
|
} from '@capacitor-firebase/authentication'
|
||||||
|
import {
|
||||||
|
getAuth,
|
||||||
|
GoogleAuthProvider,
|
||||||
|
OAuthProvider,
|
||||||
|
signInWithCredential,
|
||||||
|
} from 'firebase/auth'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
signedIn: [authResult: any]
|
signedIn: [authResult: any]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
// const ui: any = inject('firebaseAuthUI')
|
// const ui: any = inject('firebaseAuthUI')
|
||||||
@@ -15,120 +22,116 @@ const auth = getAuth()
|
|||||||
const firebaseAuthUI = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(auth)
|
const firebaseAuthUI = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(auth)
|
||||||
|
|
||||||
const uiConfig = {
|
const uiConfig = {
|
||||||
signInOptions: [
|
signInOptions: [
|
||||||
firebase.auth.EmailAuthProvider.PROVIDER_ID
|
firebase.auth.EmailAuthProvider.PROVIDER_ID
|
||||||
// firebase.auth.GoogleAuthProvider.PROVIDER_ID
|
// firebase.auth.GoogleAuthProvider.PROVIDER_ID
|
||||||
],
|
],
|
||||||
// signInFlow: 'popup',
|
// signInFlow: 'popup',
|
||||||
signInFlow: 'redirect',
|
signInFlow: 'redirect',
|
||||||
callbacks: {
|
callbacks: {
|
||||||
signInSuccessWithAuthResult(authResult: any) {
|
signInSuccessWithAuthResult(authResult: any) {
|
||||||
// var user = authResult.user
|
// var user = authResult.user
|
||||||
// var credential = authResult.credential
|
// var credential = authResult.credential
|
||||||
// var isNewUser = authResult.additionalUserInfo.isNewUser
|
// var isNewUser = authResult.additionalUserInfo.isNewUser
|
||||||
// var providerId = authResult.additionalUserInfo.providerId
|
// var providerId = authResult.additionalUserInfo.providerId
|
||||||
// var operationType = authResult.operationType
|
// var operationType = authResult.operationType
|
||||||
// Do something with the returned AuthResult.
|
// Do something with the returned AuthResult.
|
||||||
// Return type determines whether we continue the redirect
|
// Return type determines whether we continue the redirect
|
||||||
// automatically or whether we leave that to developer to handle.
|
// automatically or whether we leave that to developer to handle.
|
||||||
emit('signedIn', authResult)
|
emit('signedIn', authResult)
|
||||||
return false
|
return false
|
||||||
},
|
},
|
||||||
signInFailure(error: any) {
|
signInFailure(error: any) {
|
||||||
console.error('Error signing in', error)
|
console.error('Error signing in', error)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Other config options...
|
}
|
||||||
|
// Other config options...
|
||||||
}
|
}
|
||||||
// onMounted(() => ui.start('#auth', uiConfig))
|
// onMounted(() => ui.start('#auth', uiConfig))
|
||||||
|
|
||||||
interface Provider {
|
interface Provider {
|
||||||
name: 'google' | 'microsoft' | 'github'
|
name: 'google' | 'microsoft' | 'github'
|
||||||
icon: string
|
icon: string
|
||||||
signin: () => Promise<void>
|
signin: () => Promise<void>
|
||||||
// (options?: SignInOptions) => Promise<SignInResult>
|
// (options?: SignInOptions) => Promise<SignInResult>
|
||||||
}
|
}
|
||||||
|
|
||||||
const providers: Provider[] = [
|
const providers: Provider[] = [
|
||||||
{
|
{
|
||||||
name: 'google',
|
name: 'google',
|
||||||
icon: 'fa-brands fa-google',
|
icon: 'fa-brands fa-google',
|
||||||
signin: async () => {
|
signin: async () => {
|
||||||
const result = await FirebaseAuthentication.signInWithGoogle({
|
const result = await FirebaseAuthentication.signInWithGoogle({
|
||||||
mode: 'redirect'
|
mode: 'redirect'
|
||||||
})
|
})
|
||||||
const credential = GoogleAuthProvider.credential(result.credential?.idToken)
|
const credential = GoogleAuthProvider.credential(result.credential?.idToken)
|
||||||
await signInWithCredential(auth, credential)
|
await signInWithCredential(auth, credential)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// {
|
},
|
||||||
// name: 'microsoft',
|
{
|
||||||
// icon: 'fa-brands fa-microsoft',
|
name: 'microsoft',
|
||||||
// signin: async () => {
|
icon: 'fa-brands fa-microsoft',
|
||||||
// const result = await FirebaseAuthentication.signInWithMicrosoft({
|
signin: async () => {
|
||||||
// mode: 'redirect'
|
const result = await FirebaseAuthentication.signInWithMicrosoft({
|
||||||
// })
|
mode: 'redirect'
|
||||||
// const provider = new OAuthProvider('microsoft.com')
|
})
|
||||||
// const credential = provider.credential({
|
const provider = new OAuthProvider('microsoft.com')
|
||||||
// idToken: result.credential?.idToken,
|
const credential = provider.credential({
|
||||||
// rawNonce: result.credential?.nonce
|
idToken: result.credential?.idToken,
|
||||||
// })
|
rawNonce: result.credential?.nonce
|
||||||
// await signInWithCredential(auth, credential)
|
})
|
||||||
// }
|
await signInWithCredential(auth, credential)
|
||||||
// },
|
}
|
||||||
// {
|
},
|
||||||
// name: 'github',
|
{
|
||||||
// icon: 'fa-brands fa-github',
|
name: 'github',
|
||||||
// signin: async () => {
|
icon: 'fa-brands fa-github',
|
||||||
// const result = await FirebaseAuthentication.signInWithGithub({
|
signin: async () => {
|
||||||
// mode: 'redirect'
|
const result = await FirebaseAuthentication.signInWithGithub({
|
||||||
// })
|
mode: 'redirect'
|
||||||
// const provider = new OAuthProvider('github.com')
|
})
|
||||||
// const credential = provider.credential({
|
const provider = new OAuthProvider('github.com')
|
||||||
// idToken: result.credential?.idToken,
|
const credential = provider.credential({
|
||||||
// rawNonce: result.credential?.nonce
|
idToken: result.credential?.idToken,
|
||||||
// })
|
rawNonce: result.credential?.nonce
|
||||||
// await signInWithCredential(auth, credential)
|
})
|
||||||
// }
|
await signInWithCredential(auth, credential)
|
||||||
// }
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
// type Provider = (typeof providers)[number]
|
// type Provider = (typeof providers)[number]
|
||||||
const signInWithProvider = async (provider: Provider) => {
|
const signInWithProvider = async (provider: Provider) => {
|
||||||
provider.signin()
|
provider.signin()
|
||||||
}
|
}
|
||||||
|
|
||||||
const signingInWithEmail = ref(false)
|
const signingInWithEmail = ref(false)
|
||||||
const signInWithEmail = () => {
|
const signInWithEmail = () => {
|
||||||
firebaseAuthUI.start('#auth', uiConfig)
|
firebaseAuthUI.start('#auth', uiConfig)
|
||||||
signingInWithEmail.value = true
|
signingInWithEmail.value = true
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<template v-if="!signingInWithEmail">
|
<template v-if="!signingInWithEmail">
|
||||||
<UIButton
|
<UIButton
|
||||||
class="mx-auto !block w-[225px] max-sm:w-full"
|
class="mx-auto block w-[225px]"
|
||||||
size="sm"
|
size="sm"
|
||||||
@click="signInWithProvider(provider)"
|
@click="signInWithProvider(provider)"
|
||||||
v-for="provider in providers"
|
v-for="provider in providers"
|
||||||
:key="provider.name"
|
:key="provider.name"
|
||||||
>
|
>
|
||||||
<i class="fa-fw mr-2" :class="provider.icon"></i>
|
<i class="fa-fw mr-2" :class="provider.icon"></i>
|
||||||
Sign in with {{ provider.name }}
|
Sign in with {{ provider.name }}
|
||||||
</UIButton>
|
</UIButton>
|
||||||
<UIButton
|
<UIButton class="mx-auto block w-[225px]" size="sm" @click="signInWithEmail">
|
||||||
class="mx-auto !block w-[225px] max-sm:w-full"
|
<i class="fa-fw fa-regular fa-envelope mr-2"></i>
|
||||||
size="sm"
|
Sign in with email
|
||||||
@click="signInWithEmail"
|
</UIButton>
|
||||||
>
|
</template>
|
||||||
<i class="fa-fw fa-regular fa-envelope mr-2"></i>
|
<div id="auth"></div>
|
||||||
Sign in with email
|
<!-- <progress
|
||||||
</UIButton>
|
|
||||||
</template>
|
|
||||||
<div id="auth"></div>
|
|
||||||
<!-- <progress
|
|
||||||
v-show="props.authenticating"
|
v-show="props.authenticating"
|
||||||
class="dui-progress dui-progress-primary w-full"
|
class="dui-progress dui-progress-primary w-full"
|
||||||
></progress> -->
|
></progress> -->
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,60 +2,60 @@
|
|||||||
import { notes, findNotesByByTitle, activeNote } from '@/composables/useNotes'
|
import { notes, findNotesByByTitle, activeNote } from '@/composables/useNotes'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
autocompleteText: string
|
autocompleteText: string
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
createLink: [title: string]
|
createLink: [title: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const results = computed<Note[]>(() => {
|
const results = computed<Note[]>(() => {
|
||||||
return (props.autocompleteText ? findNotesByByTitle(props.autocompleteText) : notes.value)
|
return (props.autocompleteText ? findNotesByByTitle(props.autocompleteText) : notes.value)
|
||||||
.filter((note) => note.id !== activeNote.value?.id)
|
.filter((note) => note.id !== activeNote.value?.id)
|
||||||
.slice(0, 10)
|
.slice(0, 10)
|
||||||
})
|
})
|
||||||
|
|
||||||
const activeResult = ref<Note>()
|
const activeResult = ref<Note>()
|
||||||
|
|
||||||
const changeActiveResult = (direction: number) => {
|
const changeActiveResult = (direction: number) => {
|
||||||
const index = results.value.findIndex((note) => note.id === activeResult.value?.id)
|
const index = results.value.findIndex((note) => note.id === activeResult.value?.id)
|
||||||
const newIndex =
|
const newIndex =
|
||||||
index + direction < results.value.length
|
index + direction < results.value.length
|
||||||
? index + direction >= -1
|
? index + direction >= -1
|
||||||
? index + direction
|
? index + direction
|
||||||
: results.value.length - 1
|
: results.value.length - 1
|
||||||
: -1
|
: -1
|
||||||
activeResult.value = newIndex >= 0 ? results.value[newIndex] : undefined
|
activeResult.value = newIndex >= 0 ? results.value[newIndex] : undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleKeypress = (event: { [key: string]: number }) => {
|
const handleKeypress = (event: { [key: string]: number }) => {
|
||||||
const keyCode = event.keyCode
|
const keyCode = event.keyCode
|
||||||
const keyCodes = {
|
const keyCodes = {
|
||||||
cycle: [38, 40],
|
cycle: [38, 40],
|
||||||
confirm: [13]
|
confirm: [13]
|
||||||
}
|
}
|
||||||
if (keyCodes.cycle.includes(keyCode)) {
|
if (keyCodes.cycle.includes(keyCode)) {
|
||||||
const direction = keyCode === 38 ? -1 : 1
|
const direction = keyCode === 38 ? -1 : 1
|
||||||
changeActiveResult(direction)
|
changeActiveResult(direction)
|
||||||
} else if (keyCodes.confirm.includes(keyCode)) {
|
} else if (keyCodes.confirm.includes(keyCode)) {
|
||||||
const contextedLink = activeResult.value ? activeResult.value.title : props.autocompleteText
|
const contextedLink = activeResult.value ? activeResult.value.title : props.autocompleteText
|
||||||
emit('createLink', contextedLink)
|
emit('createLink', contextedLink)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
defineExpose({ handleKeypress })
|
defineExpose({ handleKeypress })
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<UIMenu class="border-[1px] p-2 text-[0.875rem] text-black shadow-md" compact>
|
<UIMenu class="border-[1px] p-2 text-[0.875rem] text-black shadow-md" :compact="true">
|
||||||
<UIMenuItem :active="!activeResult" @click="emit('createLink', props.autocompleteText)">
|
<UIMenuItem :active="!activeResult" @click="emit('createLink', props.autocompleteText)">
|
||||||
<span class="flex-grow">{{ props.autocompleteText }}</span>
|
<span class="flex-grow">{{ props.autocompleteText }}</span>
|
||||||
<i class="fas fa-plus-circle ml-auto text-white" />
|
<i class="fas fa-plus-circle ml-auto text-white" />
|
||||||
</UIMenuItem>
|
</UIMenuItem>
|
||||||
<SearchResult
|
<SearchResult
|
||||||
v-for="result in results"
|
v-for="result in results"
|
||||||
:key="result.id"
|
:key="result.id"
|
||||||
:result="result"
|
:result="result"
|
||||||
:active-result="activeResult"
|
:active-result="activeResult"
|
||||||
@go-to-note="emit('createLink', result.title)"
|
@go-to-note="emit('createLink', result.title)"
|
||||||
/>
|
/>
|
||||||
</UIMenu>
|
</UIMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -15,11 +15,12 @@ import ContextedPlugin from '@/ckeditor/ContextedPlugin'
|
|||||||
import { mdToHtml, htmlToMd } from '@/utils/markdown'
|
import { mdToHtml, htmlToMd } from '@/utils/markdown'
|
||||||
import { getNoteByTitle, setActiveNote, addNote } from '@/composables/useNotes'
|
import { getNoteByTitle, setActiveNote, addNote } from '@/composables/useNotes'
|
||||||
import Autocomplete from '@/components/Note/Autocomplete.vue'
|
import Autocomplete from '@/components/Note/Autocomplete.vue'
|
||||||
import { vibrate } from '@/composables/useHaptics'
|
import { Haptics, ImpactStyle } from '@capacitor/haptics'
|
||||||
|
|
||||||
const props = defineProps<{ note: Note }>()
|
const props = defineProps<{ note: Note }>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
update: [mdText: string]
|
update: [mdText: string]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const html = mdToHtml(props.note.content)
|
const html = mdToHtml(props.note.content)
|
||||||
@@ -27,34 +28,34 @@ const html = mdToHtml(props.note.content)
|
|||||||
const editor = BalloonEditor
|
const editor = BalloonEditor
|
||||||
const editorData = ref<string>(html)
|
const editorData = ref<string>(html)
|
||||||
const editorConfig = {
|
const editorConfig = {
|
||||||
plugins: [
|
plugins: [
|
||||||
EssentialsPlugin,
|
EssentialsPlugin,
|
||||||
BoldPlugin,
|
BoldPlugin,
|
||||||
ItalicPlugin,
|
ItalicPlugin,
|
||||||
UnderlinePlugin,
|
UnderlinePlugin,
|
||||||
StrikethroughPlugin,
|
StrikethroughPlugin,
|
||||||
LinkPlugin,
|
LinkPlugin,
|
||||||
HeadingPlugin,
|
HeadingPlugin,
|
||||||
ParagraphPlugin,
|
ParagraphPlugin,
|
||||||
ListPlugin,
|
ListPlugin,
|
||||||
AutoformatPlugin,
|
AutoformatPlugin,
|
||||||
ContextedPlugin
|
ContextedPlugin
|
||||||
],
|
],
|
||||||
toolbar: {
|
toolbar: {
|
||||||
items: [
|
items: [
|
||||||
'bold',
|
'bold',
|
||||||
'italic',
|
'italic',
|
||||||
'underline',
|
'underline',
|
||||||
'strikethrough',
|
'strikethrough',
|
||||||
'link',
|
'link',
|
||||||
'undo',
|
'undo',
|
||||||
'redo',
|
'redo',
|
||||||
'heading',
|
'heading',
|
||||||
'bulletedList',
|
'bulletedList',
|
||||||
'numberedList'
|
'numberedList'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
placeholder: 'Click here to start typing...'
|
placeholder: 'Click here to start typing...'
|
||||||
}
|
}
|
||||||
|
|
||||||
const editorElement = ref<HTMLInputElement | null>(null)
|
const editorElement = ref<HTMLInputElement | null>(null)
|
||||||
@@ -62,12 +63,12 @@ watch(editorData, () => emit('update', htmlToMd(editorData.value)))
|
|||||||
let editorInstance: any
|
let editorInstance: any
|
||||||
|
|
||||||
const handleClick = async ({ data }: { data: any }) => {
|
const handleClick = async ({ data }: { data: any }) => {
|
||||||
if (!data.domTarget.hasAttribute('data-contexted-link')) return
|
if (!data.domTarget.hasAttribute('data-contexted-link')) return
|
||||||
const noteTitle = data.domTarget.textContent as string
|
const noteTitle = data.domTarget.textContent as string
|
||||||
let note: BaseNote | Note | undefined = getNoteByTitle(noteTitle)
|
let note: BaseNote | Note | undefined = getNoteByTitle(noteTitle)
|
||||||
if (!note) note = addNote(noteTitle, '')
|
if (!note) note = addNote(noteTitle, '')
|
||||||
setActiveNote(note.id)
|
setActiveNote(note.id)
|
||||||
await vibrate()
|
await Haptics.impact({ style: ImpactStyle.Light })
|
||||||
}
|
}
|
||||||
|
|
||||||
const autocompleteRef = ref<InstanceType<typeof Autocomplete> | null>(null)
|
const autocompleteRef = ref<InstanceType<typeof Autocomplete> | null>(null)
|
||||||
@@ -77,108 +78,108 @@ const autocompleteText = ref<string>('')
|
|||||||
const autocompleteReverse = ref<boolean>(false)
|
const autocompleteReverse = ref<boolean>(false)
|
||||||
|
|
||||||
const handleAutocomplete = async (event: AutocompleteEvent) => {
|
const handleAutocomplete = async (event: AutocompleteEvent) => {
|
||||||
const position = event.position
|
const position = event.position
|
||||||
if (position && editorElement.value) {
|
if (position && editorElement.value) {
|
||||||
const rect: any = editorElement.value?.getBoundingClientRect()
|
const rect: any = editorElement.value?.getBoundingClientRect()
|
||||||
const lineHeight = parseFloat(
|
const lineHeight = parseFloat(
|
||||||
window.getComputedStyle(event.domElement || editorElement.value).lineHeight
|
window.getComputedStyle(event.domElement || editorElement.value).lineHeight
|
||||||
)
|
)
|
||||||
autocompleteStyle.value = {
|
autocompleteStyle.value = {
|
||||||
top: `${position.top - rect.top + lineHeight}px`,
|
top: `${position.top - rect.top + lineHeight}px`,
|
||||||
left: `${position.left - rect.left}px`
|
left: `${position.left - rect.left}px`
|
||||||
}
|
|
||||||
}
|
}
|
||||||
autocompleteText.value = event.autocompleteText || ''
|
}
|
||||||
showAutocomplete.value = event.show
|
autocompleteText.value = event.autocompleteText || ''
|
||||||
await nextTick()
|
showAutocomplete.value = event.show
|
||||||
const autocompleteElem = autocompleteRef.value?.$el
|
await nextTick()
|
||||||
const autocompleteRect = autocompleteRef.value?.$el.getBoundingClientRect()
|
const autocompleteElem = autocompleteRef.value?.$el
|
||||||
const editorRect = editorElement.value?.getBoundingClientRect()
|
const autocompleteRect = autocompleteRef.value?.$el.getBoundingClientRect()
|
||||||
if (
|
const editorRect = editorElement.value?.getBoundingClientRect()
|
||||||
autocompleteElem &&
|
if (
|
||||||
autocompleteRect &&
|
autocompleteElem &&
|
||||||
editorRect &&
|
autocompleteRect &&
|
||||||
autocompleteRect.bottom > editorRect.bottom
|
editorRect &&
|
||||||
) {
|
autocompleteRect.bottom > editorRect.bottom
|
||||||
const autocompleteHeight = parseFloat(window.getComputedStyle(autocompleteElem).height)
|
) {
|
||||||
autocompleteStyle.value = {
|
const autocompleteHeight = parseFloat(window.getComputedStyle(autocompleteElem).height)
|
||||||
...autocompleteStyle.value,
|
autocompleteStyle.value = {
|
||||||
top: `${position.top - editorRect.top - autocompleteHeight}px`
|
...autocompleteStyle.value,
|
||||||
}
|
top: `${position.top - editorRect.top - autocompleteHeight}px`
|
||||||
autocompleteReverse.value = true
|
|
||||||
} else {
|
|
||||||
autocompleteReverse.value = false
|
|
||||||
}
|
}
|
||||||
|
autocompleteReverse.value = true
|
||||||
|
} else {
|
||||||
|
autocompleteReverse.value = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleContextedKeypress = (event: any) => {
|
const handleContextedKeypress = (event: any) => {
|
||||||
if (autocompleteRef.value) autocompleteRef.value.handleKeypress(event)
|
if (autocompleteRef.value) autocompleteRef.value.handleKeypress(event)
|
||||||
}
|
}
|
||||||
|
|
||||||
const createLink = (link: string) => {
|
const createLink = (link: string) => {
|
||||||
if (!editor) return
|
if (!editor) return
|
||||||
const model = editorInstance.model
|
const model = editorInstance.model
|
||||||
const getPosition = () => model.document.selection.anchor
|
const getPosition = () => model.document.selection.anchor
|
||||||
// const getNodes = () => {
|
// const getNodes = () => {
|
||||||
// const nodes = [
|
// const nodes = [
|
||||||
// getPosition().nodeBefore,
|
// getPosition().nodeBefore,
|
||||||
// getPosition().nodeAfter,
|
// getPosition().nodeAfter,
|
||||||
// getPosition().textNnode,
|
// getPosition().textNnode,
|
||||||
// ]
|
// ]
|
||||||
// return nodes.map((node) => ({
|
// return nodes.map((node) => ({
|
||||||
// data: node?.data,
|
// data: node?.data,
|
||||||
// attrs: Array.from(node?.getAttributes() || []),
|
// attrs: Array.from(node?.getAttributes() || []),
|
||||||
// }))
|
// }))
|
||||||
// }
|
// }
|
||||||
// console.log(getNodes())
|
// console.log(getNodes())
|
||||||
let nodeToRemove: any
|
let nodeToRemove: any
|
||||||
if (getPosition().nodeBefore?.hasAttribute('autocomplete')) {
|
if (getPosition().nodeBefore?.hasAttribute('autocomplete')) {
|
||||||
// Insert new link
|
// Insert new link
|
||||||
nodeToRemove = getPosition().nodeBefore
|
nodeToRemove = getPosition().nodeBefore
|
||||||
} else if (getPosition().nodeBefore?.hasAttribute('contextedLink')) {
|
} else if (getPosition().nodeBefore?.hasAttribute('contextedLink')) {
|
||||||
// Update existing link from end of existing link (backspace)
|
// Update existing link from end of existing link (backspace)
|
||||||
nodeToRemove = getPosition().nodeBefore
|
nodeToRemove = getPosition().nodeBefore
|
||||||
} else if (getPosition().textNode?.hasAttribute('contextedLink')) {
|
} else if (getPosition().textNode?.hasAttribute('contextedLink')) {
|
||||||
// Update existing link from middle of existing link
|
// Update existing link from middle of existing link
|
||||||
nodeToRemove = getPosition().textNode
|
nodeToRemove = getPosition().textNode
|
||||||
} else if (getPosition().nodeAfter?.hasAttribute('contextedLink')) {
|
} else if (getPosition().nodeAfter?.hasAttribute('contextedLink')) {
|
||||||
// Update existing link from beginning (delete)
|
// Update existing link from beginning (delete)
|
||||||
nodeToRemove = getPosition().nodeAfter
|
nodeToRemove = getPosition().nodeAfter
|
||||||
}
|
}
|
||||||
|
|
||||||
model.change((writer: any) => {
|
model.change((writer: any) => {
|
||||||
if (nodeToRemove) writer.remove(nodeToRemove)
|
if (nodeToRemove) writer.remove(nodeToRemove)
|
||||||
writer.insertText(link, { contextedLink: true }, getPosition(), 'after')
|
writer.insertText(link, { contextedLink: true }, getPosition(), 'after')
|
||||||
model.enqueueChange((writer: any) => {
|
model.enqueueChange((writer: any) => {
|
||||||
const nodeAfter = getPosition().nodeAfter
|
const nodeAfter = getPosition().nodeAfter
|
||||||
if (!nodeAfter || (nodeAfter && !nodeAfter.data.startsWith(']]'))) {
|
if (!nodeAfter || (nodeAfter && !nodeAfter.data.startsWith(']]'))) {
|
||||||
writer.insertText(']]', model.document.selection.getFirstPosition())
|
writer.insertText(']]', model.document.selection.getFirstPosition())
|
||||||
}
|
}
|
||||||
})
|
|
||||||
showAutocomplete.value = false
|
|
||||||
})
|
})
|
||||||
|
showAutocomplete.value = false
|
||||||
|
})
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="relative" ref="editorElement">
|
<div class="relative" ref="editorElement">
|
||||||
<CKEditor
|
<CKEditor
|
||||||
class="w-full flex-grow text-[110%] font-light"
|
class="w-full flex-grow text-[110%] font-light"
|
||||||
:editor="editor"
|
:editor="editor"
|
||||||
v-model="editorData"
|
v-model="editorData"
|
||||||
:config="editorConfig"
|
:config="editorConfig"
|
||||||
@editor-ready="(editor) => (editorInstance = editor)"
|
@editor-ready="(editor) => (editorInstance = editor)"
|
||||||
@click="handleClick"
|
@click="handleClick"
|
||||||
@contexted-link-autocomplete="handleAutocomplete"
|
@contexted-link-autocomplete="handleAutocomplete"
|
||||||
@contexted-keypress="handleContextedKeypress"
|
@contexted-keypress="handleContextedKeypress"
|
||||||
></CKEditor>
|
></CKEditor>
|
||||||
<Autocomplete
|
<Autocomplete
|
||||||
v-if="showAutocomplete"
|
v-if="showAutocomplete"
|
||||||
ref="autocompleteRef"
|
ref="autocompleteRef"
|
||||||
:autocomplete-text="autocompleteText"
|
:autocomplete-text="autocompleteText"
|
||||||
:style="autocompleteStyle"
|
:style="autocompleteStyle"
|
||||||
@create-link="createLink"
|
@create-link="createLink"
|
||||||
class="absolute w-[300px]"
|
class="absolute w-[300px]"
|
||||||
:class="autocompleteReverse && 'flex-col-reverse'"
|
:class="autocompleteReverse && 'flex-col-reverse'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,22 +1,22 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { setActiveNote } from '@/composables/useNotes'
|
import { setActiveNote } from '@/composables/useNotes'
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
references: Note[]
|
references: Note[]
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<UIMenu class="mt-3 rounded-xl border-[1px] px-3 py-3" v-if="props.references.length > 0">
|
<UIMenu class="mt-3 rounded-xl border-[1px] px-3 py-3" v-if="props.references.length > 0">
|
||||||
<UIMenuItem title>
|
<UIMenuItem :title="true">
|
||||||
<span>References</span>
|
<span>References</span>
|
||||||
<UIBadge variant="outline" class="ml-2">{{ props.references.length }}</UIBadge>
|
<UIBadge variant="outline" class="ml-2">{{ props.references.length }}</UIBadge>
|
||||||
</UIMenuItem>
|
</UIMenuItem>
|
||||||
<UIMenuItem
|
<UIMenuItem
|
||||||
v-for="reference in props.references"
|
v-for="reference in props.references"
|
||||||
:key="reference.id"
|
:key="reference.id"
|
||||||
@click="setActiveNote(reference.id)"
|
@click="setActiveNote(reference.id)"
|
||||||
>
|
>
|
||||||
<i class="far fa-file-alt fa-fw" />
|
<i class="far fa-file-alt fa-fw" />
|
||||||
{{ reference.title }}
|
{{ reference.title }}
|
||||||
</UIMenuItem>
|
</UIMenuItem>
|
||||||
</UIMenu>
|
</UIMenu>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -2,87 +2,77 @@
|
|||||||
import { Capacitor } from '@capacitor/core'
|
import { Capacitor } from '@capacitor/core'
|
||||||
import { Dialog } from '@capacitor/dialog'
|
import { Dialog } from '@capacitor/dialog'
|
||||||
import type { ConfirmOptions } from '@capacitor/dialog'
|
import type { ConfirmOptions } from '@capacitor/dialog'
|
||||||
import { vibrate } from '@/composables/useHaptics'
|
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: Note
|
note: Note
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
type ActionKey = 'delete' | 'setRoot'
|
type ActionKey = 'delete' | 'setRoot'
|
||||||
|
|
||||||
interface ModalOptions {
|
interface ModalOptions {
|
||||||
key: ActionKey
|
key: ActionKey
|
||||||
icon: string
|
icon: string
|
||||||
confirmOptions: ConfirmOptions
|
confirmOptions: ConfirmOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmModals: ModalOptions[] = [
|
const confirmModals: ModalOptions[] = [
|
||||||
{
|
{
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
icon: 'fas fa-fw fa-trash',
|
icon: 'fas fa-fw fa-trash',
|
||||||
confirmOptions: {
|
confirmOptions: {
|
||||||
title: 'Delete note',
|
title: 'Delete note',
|
||||||
message: 'Are you sure you want to delete this note?',
|
message: 'Are you sure you want to delete this note?',
|
||||||
okButtonTitle: 'Delete note'
|
okButtonTitle: 'Delete note'
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'setRoot',
|
|
||||||
icon: 'fas fa-fw fa-sitemap',
|
|
||||||
confirmOptions: {
|
|
||||||
title: 'Set root note',
|
|
||||||
message: 'Are you sure you want to set this note as root note?',
|
|
||||||
okButtonTitle: 'Set note as root note'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'setRoot',
|
||||||
|
icon: 'fas fa-fw fa-sitemap',
|
||||||
|
confirmOptions: {
|
||||||
|
title: 'Set root note',
|
||||||
|
message: 'Are you sure you want to set this note as root note?',
|
||||||
|
okButtonTitle: 'Set note as root note'
|
||||||
|
}
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
execute: [actionType: ActionKey, close?: () => Promise<void>]
|
execute: [actionType: ActionKey, close?: () => void]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const openModal = async (open: () => void, modal: ModalOptions) => {
|
const openModal = async (open: () => void, modal: ModalOptions) => {
|
||||||
if (['android', 'ios'].includes(Capacitor.getPlatform())) {
|
if (['android', 'ios'].includes(Capacitor.getPlatform())) {
|
||||||
const { value: confirmed } = await Dialog.confirm(modal.confirmOptions)
|
const { value: confirmed } = await Dialog.confirm(modal.confirmOptions)
|
||||||
if (confirmed) emit('execute', modal.key)
|
if (confirmed) emit('execute', modal.key)
|
||||||
} else {
|
} else {
|
||||||
open()
|
open()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="mb-2 flex items-center space-x-2">
|
<div class="mb-2 flex items-center space-x-2">
|
||||||
<h1 class="flex flex-grow items-center rounded-md text-3xl font-semibold hover:bg-gray-200">
|
<h1 class="flex flex-grow items-center rounded-md text-3xl font-semibold hover:bg-gray-200">
|
||||||
<slot name="title"></slot>
|
<slot name="title"></slot>
|
||||||
</h1>
|
</h1>
|
||||||
<UIButtonGroup class="flex items-center" v-if="!props.note.isRoot">
|
<UIButtonGroup class="flex items-center" v-if="!props.note.isRoot">
|
||||||
<UIModal v-for="confirmModal in confirmModals" :key="confirmModal.key">
|
<UIModal v-for="confirmModal in confirmModals" :key="confirmModal.key">
|
||||||
<template #activator="{ open }">
|
<template #activator="{ open }">
|
||||||
<UIButton
|
<UIButton size="sm" @click="openModal(open, confirmModal)" :join="true">
|
||||||
size="sm"
|
<i :class="confirmModal.icon" />
|
||||||
@click="openModal(open, confirmModal)"
|
</UIButton>
|
||||||
@mousedown="vibrate"
|
</template>
|
||||||
join
|
<template #title>
|
||||||
>
|
<i class="mr-2" :class="confirmModal.icon" />
|
||||||
<i :class="confirmModal.icon" />
|
{{ confirmModal.confirmOptions.title }}
|
||||||
</UIButton>
|
</template>
|
||||||
</template>
|
<template #default>{{ confirmModal.confirmOptions.message }}</template>
|
||||||
<template #title>
|
<template #actions="{ close }">
|
||||||
<i class="mr-2" :class="confirmModal.icon" />
|
<UIButton size="sm" @click="close">Cancel</UIButton>
|
||||||
{{ confirmModal.confirmOptions.title }}
|
<UIButton size="sm" color="primary" @click="emit('execute', confirmModal.key, close)">
|
||||||
</template>
|
{{ confirmModal.confirmOptions.okButtonTitle }}
|
||||||
<template #default>{{ confirmModal.confirmOptions.message }}</template>
|
</UIButton>
|
||||||
<template #actions="{ close }">
|
</template>
|
||||||
<UIButton size="sm" @click="close">Cancel</UIButton>
|
</UIModal>
|
||||||
<UIButton
|
</UIButtonGroup>
|
||||||
size="sm"
|
</div>
|
||||||
color="primary"
|
|
||||||
@click="emit('execute', confirmModal.key, close)"
|
|
||||||
>
|
|
||||||
{{ confirmModal.confirmOptions.okButtonTitle }}
|
|
||||||
</UIButton>
|
|
||||||
</template>
|
|
||||||
</UIModal>
|
|
||||||
</UIButtonGroup>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,85 +3,84 @@ import { notes, findNotes, setActiveNote } from '@/composables/useNotes'
|
|||||||
import SearchResult from '@/components/Search/SearchResult.vue'
|
import SearchResult from '@/components/Search/SearchResult.vue'
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
active: [active: boolean]
|
active: [active: boolean]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const active = ref<boolean>(false)
|
const active = ref<boolean>(false)
|
||||||
watch(active, () => {
|
watch(active, () => {
|
||||||
if (!active.value) {
|
if (!active.value) {
|
||||||
query.value = ''
|
query.value = ''
|
||||||
activeResult.value = undefined
|
activeResult.value = undefined
|
||||||
}
|
}
|
||||||
emit('active', active.value)
|
emit('active', active.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const query = ref<string>('')
|
const query = ref<string>('')
|
||||||
const results = computed<Note[]>(() => {
|
const results = computed<Note[]>(() => {
|
||||||
return query.value ? findNotes(query.value) : notes.value
|
return query.value ? findNotes(query.value) : notes.value
|
||||||
})
|
})
|
||||||
|
|
||||||
const goToNote = (note: Note) => {
|
const goToNote = (note: Note) => {
|
||||||
setActiveNote(note.id)
|
setActiveNote(note.id)
|
||||||
active.value = false
|
active.value = false
|
||||||
if (queryElem.value) queryElem.value.blur()
|
if (queryElem.value) queryElem.value.blur()
|
||||||
}
|
}
|
||||||
const queryElem = ref<HTMLInputElement | null>(null)
|
const queryElem = ref<HTMLInputElement | null>(null)
|
||||||
|
|
||||||
const activeResult = ref<Note>()
|
const activeResult = ref<Note>()
|
||||||
|
|
||||||
const handleKeydown = (event: KeyboardEvent) => {
|
const handleKeydown = (event: KeyboardEvent) => {
|
||||||
const code = event.code
|
const code = event.code
|
||||||
if (['ArrowUp', 'ArrowDown', 'Tab'].includes(code)) {
|
if (['ArrowUp', 'ArrowDown', 'Tab'].includes(code)) {
|
||||||
let index = results.value.findIndex((note) => note.id === activeResult.value?.id)
|
let index = results.value.findIndex((note) => note.id === activeResult.value?.id)
|
||||||
if (['ArrowDown', 'Tab'].includes(code)) {
|
if (['ArrowDown', 'Tab'].includes(code)) {
|
||||||
index++
|
index++
|
||||||
} else if (['ArrowUp'].includes(code)) {
|
} else if (['ArrowUp'].includes(code)) {
|
||||||
index--
|
index--
|
||||||
}
|
|
||||||
if (index + 1 > results.value.length) index = index - results.value.length
|
|
||||||
if (index < 0) index = results.value.length - 1
|
|
||||||
activeResult.value = results.value[index]
|
|
||||||
const element = resultsRefs.value[index].$el
|
|
||||||
if (['ArrowUp', 'ArrowDown', 'Tab'].includes(code))
|
|
||||||
element.scrollIntoView({ block: 'nearest' })
|
|
||||||
} else if (code === 'Enter' && activeResult.value) {
|
|
||||||
goToNote(activeResult.value)
|
|
||||||
} else if (code === 'Escape' && queryElem.value) {
|
|
||||||
queryElem.value.blur()
|
|
||||||
}
|
}
|
||||||
|
if (index + 1 > results.value.length) index = index - results.value.length
|
||||||
|
if (index < 0) index = results.value.length - 1
|
||||||
|
activeResult.value = results.value[index]
|
||||||
|
const element = resultsRefs.value[index].$el
|
||||||
|
if (['ArrowUp', 'ArrowDown', 'Tab'].includes(code)) element.scrollIntoView({ block: 'nearest' })
|
||||||
|
} else if (code === 'Enter' && activeResult.value) {
|
||||||
|
goToNote(activeResult.value)
|
||||||
|
} else if (code === 'Escape' && queryElem.value) {
|
||||||
|
queryElem.value.blur()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const resultsRefs = ref<InstanceType<typeof SearchResult>[]>([])
|
const resultsRefs = ref<InstanceType<typeof SearchResult>[]>([])
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div id="search-container" class="relative h-full flex-grow">
|
<div id="search-container" class="relative h-full flex-grow">
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
placeholder="Search for notes"
|
placeholder="Search for notes"
|
||||||
class="h-full w-full rounded border-0 bg-white/10 px-2 text-white outline-none placeholder:text-white focus:bg-white focus:text-black"
|
class="h-full w-full rounded border-0 bg-white/10 px-2 text-white outline-none placeholder:text-white focus:bg-white focus:text-black"
|
||||||
@focus="active = true"
|
@focus="active = true"
|
||||||
@mousedown="active = true"
|
@mousedown="active = true"
|
||||||
@blur="active = false"
|
@blur="active = false"
|
||||||
v-model="query"
|
v-model="query"
|
||||||
ref="queryElem"
|
ref="queryElem"
|
||||||
@keydown="handleKeydown"
|
@keydown="handleKeydown"
|
||||||
/>
|
/>
|
||||||
<div class="z-1000 absolute left-0 right-0 top-[100%]" v-if="active">
|
<div class="z-1000 absolute left-0 right-0 top-[100%]" v-if="active">
|
||||||
<UIMenu compact class="mt-1 w-full rounded-md bg-base-100 p-2 text-black shadow">
|
<UIMenu :compact="true" class="mt-1 w-full rounded-md bg-base-100 p-2 text-black shadow">
|
||||||
<div class="max-h-[320px] w-full overflow-y-auto">
|
<div class="max-h-[320px] w-full overflow-y-auto">
|
||||||
<template v-if="results.length > 0">
|
<template v-if="results.length > 0">
|
||||||
<SearchResult
|
<SearchResult
|
||||||
v-for="result in results"
|
v-for="result in results"
|
||||||
:key="result.id"
|
:key="result.id"
|
||||||
:result="result"
|
:result="result"
|
||||||
:active-result="activeResult"
|
:active-result="activeResult"
|
||||||
@go-to-note="goToNote(result)"
|
@go-to-note="goToNote(result)"
|
||||||
ref="resultsRefs"
|
ref="resultsRefs"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
<UIMenuItem :compact="true" v-else>No notes found</UIMenuItem>
|
<UIMenuItem :compact="true" v-else>No notes found</UIMenuItem>
|
||||||
</div>
|
|
||||||
</UIMenu>
|
|
||||||
</div>
|
</div>
|
||||||
|
</UIMenu>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,28 +3,28 @@ import { activeNote } from '@/composables/useNotes'
|
|||||||
import { formatDate } from '@/utils/helpers'
|
import { formatDate } from '@/utils/helpers'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
result: Note
|
result: Note
|
||||||
activeResult?: Note
|
activeResult?: Note
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
goToNote: [element: HTMLElement | null]
|
goToNote: [element: HTMLElement | null]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const element = ref<HTMLElement | null>(null)
|
const element = ref<HTMLElement | null>(null)
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<UIMenuItem
|
<UIMenuItem
|
||||||
class="flex w-full items-center"
|
class="flex w-full items-center"
|
||||||
@click.stop.prevent="() => emit('goToNote', element)"
|
@click.stop.prevent="() => emit('goToNote', element)"
|
||||||
@mousedown.prevent
|
@mousedown.prevent
|
||||||
:disabled="activeNote?.id === result.id"
|
:disabled="activeNote?.id === result.id"
|
||||||
:active="props.activeResult?.id === result.id"
|
:active="props.activeResult?.id === result.id"
|
||||||
>
|
>
|
||||||
<UIBadge size="sm" variant="ghost" class="mr-0.5" v-if="activeNote?.id === result.id">
|
<UIBadge size="sm" variant="ghost" class="mr-0.5" v-if="activeNote?.id === result.id">
|
||||||
current
|
current
|
||||||
</UIBadge>
|
</UIBadge>
|
||||||
<span class="flex-grow truncate">{{ result.title }}</span>
|
<span class="flex-grow truncate">{{ result.title }}</span>
|
||||||
<span class="whitespace-nowrap">{{ formatDate(result.modified) }}</span>
|
<span class="whitespace-nowrap">{{ formatDate(result.modified) }}</span>
|
||||||
</UIMenuItem>
|
</UIMenuItem>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -6,80 +6,80 @@ import { activeViewMode } from '@/composables/useViewMode'
|
|||||||
const loading = inject<boolean>('loading')
|
const loading = inject<boolean>('loading')
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
viewModes: ViewMode[]
|
viewModes: ViewMode[]
|
||||||
activeViewMode: ViewMode
|
activeViewMode: ViewMode
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
setViewMode: [viewMode: ViewMode]
|
setViewMode: [viewMode: ViewMode]
|
||||||
collapse: [collapse: boolean]
|
collapse: [collapse: boolean]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const setActiveNote = (noteId: string | undefined) => {
|
const setActiveNote = (noteId: string | undefined) => {
|
||||||
emit('collapse', windowIsMobile())
|
emit('collapse', windowIsMobile())
|
||||||
baseSetActiveNote(noteId)
|
baseSetActiveNote(noteId)
|
||||||
}
|
}
|
||||||
|
|
||||||
const setViewMode = (viewMode: ViewMode) => {
|
const setViewMode = (viewMode: ViewMode) => {
|
||||||
emit('collapse', windowIsMobile())
|
emit('collapse', windowIsMobile())
|
||||||
emit('setViewMode', viewMode)
|
emit('setViewMode', viewMode)
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
id="sidebar"
|
id="sidebar"
|
||||||
class="fixed bottom-0 top-0 flex flex-col gap-4 overflow-y-auto px-2 py-3 text-[90%] max-sm:w-sidebar-mobile max-sm:gap-6 max-sm:text-[110%] sm:w-sidebar"
|
class="fixed bottom-0 top-0 flex flex-col gap-4 overflow-y-auto px-2 py-3 text-[90%] max-sm:w-sidebar-mobile max-sm:gap-6 max-sm:text-[110%] sm:w-sidebar"
|
||||||
>
|
>
|
||||||
<SideBarMenu>
|
<SideBarMenu>
|
||||||
<template #header>Root note</template>
|
<template #header>Root note</template>
|
||||||
<template #items>
|
<template #items>
|
||||||
<SideBarMenuItem
|
<SideBarMenuItem
|
||||||
icon="fas fa-fw fa-home"
|
icon="fas fa-fw fa-home"
|
||||||
@click="setActiveNote(rootNote?.id)"
|
@click="setActiveNote(rootNote?.id)"
|
||||||
:title="rootNote?.title"
|
:title="rootNote?.title"
|
||||||
v-if="!loading"
|
v-if="!loading"
|
||||||
>
|
>
|
||||||
{{ rootNote?.title }}
|
{{ rootNote?.title }}
|
||||||
</SideBarMenuItem>
|
</SideBarMenuItem>
|
||||||
<SkeletonSidebarItem v-else />
|
<SkeletonSidebarItem v-else />
|
||||||
</template>
|
</template>
|
||||||
</SideBarMenu>
|
</SideBarMenu>
|
||||||
<SideBarMenu>
|
<SideBarMenu>
|
||||||
<template #header>View mode</template>
|
<template #header>View mode</template>
|
||||||
<template #items>
|
<template #items>
|
||||||
<template v-if="!loading">
|
<template v-if="!loading">
|
||||||
<SideBarMenuItem
|
<SideBarMenuItem
|
||||||
v-for="viewMode in props.viewModes"
|
v-for="viewMode in props.viewModes"
|
||||||
:key="viewMode.name"
|
:key="viewMode.name"
|
||||||
:icon="viewMode.icon"
|
:icon="viewMode.icon"
|
||||||
:active="viewMode.name === activeViewMode.name"
|
:active="viewMode.name === activeViewMode.name"
|
||||||
@click="setViewMode(viewMode)"
|
@click="setViewMode(viewMode)"
|
||||||
>
|
>
|
||||||
{{ viewMode.name }}
|
{{ viewMode.name }}
|
||||||
</SideBarMenuItem>
|
</SideBarMenuItem>
|
||||||
</template>
|
</template>
|
||||||
<SkeletonSidebarItem :n="3" v-else />
|
<SkeletonSidebarItem :n="3" v-else />
|
||||||
</template>
|
</template>
|
||||||
</SideBarMenu>
|
</SideBarMenu>
|
||||||
<SideBarMenu>
|
<SideBarMenu>
|
||||||
<template #header>
|
<template #header>
|
||||||
<i class="far fa-clock fa-fw mr-2" />
|
<i class="far fa-clock fa-fw mr-2" />
|
||||||
Recent notes
|
Recent notes
|
||||||
</template>
|
</template>
|
||||||
<template #items>
|
<template #items>
|
||||||
<template v-if="!loading">
|
<template v-if="!loading">
|
||||||
<SideBarMenuItem
|
<SideBarMenuItem
|
||||||
v-for="note in notes.slice(-5)"
|
v-for="note in notes.slice(-5)"
|
||||||
:key="note.id"
|
:key="note.id"
|
||||||
icon="far fa-file-alt fa-fw"
|
icon="far fa-file-alt fa-fw"
|
||||||
@click="setActiveNote(note.id)"
|
@click="setActiveNote(note.id)"
|
||||||
:title="rootNote?.title"
|
:title="rootNote?.title"
|
||||||
>
|
>
|
||||||
{{ note.title }}
|
{{ note.title }}
|
||||||
</SideBarMenuItem>
|
</SideBarMenuItem>
|
||||||
</template>
|
</template>
|
||||||
<SkeletonSidebarItem v-else :n="5" />
|
<SkeletonSidebarItem v-else :n="5" />
|
||||||
</template>
|
</template>
|
||||||
</SideBarMenu>
|
</SideBarMenu>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-sm font-semibold uppercase text-secondary">
|
<div class="text-sm font-semibold uppercase text-secondary">
|
||||||
<slot name="header"></slot>
|
<slot name="header"></slot>
|
||||||
</div>
|
|
||||||
<slot name="items"></slot>
|
|
||||||
</div>
|
</div>
|
||||||
|
<slot name="items"></slot>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
icon?: string
|
icon?: string
|
||||||
active?: boolean
|
active?: boolean
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<a
|
<a
|
||||||
class="mt-1 block w-full cursor-pointer truncate rounded hover:bg-gray-200 active:bg-primary active:text-primary-content max-sm:mt-2"
|
class="mt-1 block w-full cursor-pointer truncate rounded hover:bg-gray-200 active:bg-primary active:text-primary-content max-sm:mt-2"
|
||||||
:class="props.active ? 'font-bold text-primary' : 'text-secondary'"
|
:class="props.active ? 'font-bold text-primary' : 'text-secondary'"
|
||||||
>
|
>
|
||||||
<i :class="props.icon" class="mr-2" v-if="props.icon"></i>
|
<i :class="props.icon" class="mr-2" v-if="props.icon"></i>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,30 +1,30 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex h-full w-full animate-pulse flex-col">
|
<div class="flex h-full w-full animate-pulse flex-col">
|
||||||
<div class="mb-2 flex items-center space-x-4 py-1">
|
<div class="mb-2 flex items-center space-x-4 py-1">
|
||||||
<div class="h-[2.25rem] w-[40px] rounded bg-secondary"></div>
|
<div class="h-[2.25rem] w-[40px] rounded bg-secondary"></div>
|
||||||
<div class="h-[2.25rem] flex-grow rounded bg-secondary"></div>
|
<div class="h-[2.25rem] flex-grow rounded bg-secondary"></div>
|
||||||
</div>
|
|
||||||
<div class="flex flex-grow flex-col gap-2">
|
|
||||||
<div class="my-1 h-[1.25rem] w-full rounded bg-secondary"></div>
|
|
||||||
<div class="my-1 h-[1.25rem] w-full rounded bg-secondary"></div>
|
|
||||||
<div class="my-1 h-[1.25rem] w-5/12 rounded bg-secondary"></div>
|
|
||||||
<div class="mt-2 h-[2rem] w-full rounded bg-secondary"></div>
|
|
||||||
<div class="my-1 h-[1.25rem] w-full rounded bg-secondary"></div>
|
|
||||||
<div class="my-1 h-[1.25rem] w-4/6 rounded bg-secondary"></div>
|
|
||||||
<div class="my-1 ml-8 h-[1.25rem] w-5/12 rounded bg-secondary"></div>
|
|
||||||
<div class="my-1 ml-8 h-[1.25rem] w-7/12 rounded bg-secondary"></div>
|
|
||||||
<div class="my-1 ml-8 h-[1.25rem] w-6/12 rounded bg-secondary"></div>
|
|
||||||
<div class="my-1 h-[1.25rem] w-full rounded bg-secondary"></div>
|
|
||||||
</div>
|
|
||||||
<hr class="my-3" />
|
|
||||||
<div class="flex gap-2">
|
|
||||||
<div class="h-[1.25rem] w-2/12 rounded bg-secondary"></div>
|
|
||||||
<div class="ml-auto h-[1.25rem] w-4/12 rounded bg-secondary"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex flex-grow flex-col gap-2">
|
||||||
|
<div class="my-1 h-[1.25rem] w-full rounded bg-secondary"></div>
|
||||||
|
<div class="my-1 h-[1.25rem] w-full rounded bg-secondary"></div>
|
||||||
|
<div class="my-1 h-[1.25rem] w-5/12 rounded bg-secondary"></div>
|
||||||
|
<div class="mt-2 h-[2rem] w-full rounded bg-secondary"></div>
|
||||||
|
<div class="my-1 h-[1.25rem] w-full rounded bg-secondary"></div>
|
||||||
|
<div class="my-1 h-[1.25rem] w-4/6 rounded bg-secondary"></div>
|
||||||
|
<div class="my-1 ml-8 h-[1.25rem] w-5/12 rounded bg-secondary"></div>
|
||||||
|
<div class="my-1 ml-8 h-[1.25rem] w-7/12 rounded bg-secondary"></div>
|
||||||
|
<div class="my-1 ml-8 h-[1.25rem] w-6/12 rounded bg-secondary"></div>
|
||||||
|
<div class="my-1 h-[1.25rem] w-full rounded bg-secondary"></div>
|
||||||
|
</div>
|
||||||
|
<hr class="my-3" />
|
||||||
|
<div class="flex gap-2">
|
||||||
|
<div class="h-[1.25rem] w-2/12 rounded bg-secondary"></div>
|
||||||
|
<div class="ml-auto h-[1.25rem] w-4/12 rounded bg-secondary"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.bg-secondary {
|
.bg-secondary {
|
||||||
@apply bg-secondary/25;
|
@apply bg-secondary/25;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,22 +1,18 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
n?: number
|
n?: number
|
||||||
}>(),
|
}>(),
|
||||||
{ n: 1 }
|
{ n: 1 }
|
||||||
)
|
)
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex w-full animate-pulse flex-col">
|
<div class="flex w-full animate-pulse flex-col">
|
||||||
<div
|
<div class="mt-1 h-[1.35rem] w-full rounded bg-secondary" v-for="i in props.n" :key="i"></div>
|
||||||
class="mt-1 h-[1.35rem] w-full rounded bg-secondary"
|
</div>
|
||||||
v-for="i in props.n"
|
|
||||||
:key="i"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.bg-secondary {
|
.bg-secondary {
|
||||||
@apply bg-secondary/25;
|
@apply bg-secondary/25;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex h-full w-full animate-pulse space-x-2">
|
<div class="flex h-full w-full animate-pulse space-x-2">
|
||||||
<div class="h-full w-full rounded bg-white/10" />
|
<div class="h-full w-full rounded bg-white/10" />
|
||||||
<div class="h-full w-[44px] rounded bg-white/10" />
|
<div class="h-full w-[44px] rounded bg-white/10" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -6,17 +6,17 @@ import { initialized } from '@/composables/useFirebase'
|
|||||||
const loading = inject<boolean>('loading')
|
const loading = inject<boolean>('loading')
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
sideBarCollapsed: boolean
|
sideBarCollapsed: boolean
|
||||||
height?: number
|
height?: number
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
height: 52
|
height: 52
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
toggleSideBar: []
|
toggleSideBar: []
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const searchActive = ref<boolean>(false)
|
const searchActive = ref<boolean>(false)
|
||||||
@@ -24,91 +24,90 @@ const searchActive = ref<boolean>(false)
|
|||||||
// const authUI: any = inject('firebaseAuthUI')
|
// const authUI: any = inject('firebaseAuthUI')
|
||||||
// const authPending = ref<boolean>(authUI.isPendingRedirect())
|
// const authPending = ref<boolean>(authUI.isPendingRedirect())
|
||||||
|
|
||||||
const handleSignIn = async (close: () => Promise<void>) => {
|
const handleSignIn = async (close: () => Promise<boolean>) => {
|
||||||
await close()
|
await close()
|
||||||
// authPending.value = false
|
// authPending.value = false
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="z-[500] flex items-end bg-primary" :class="searchActive && 'search-active'">
|
<div class="z-[500] flex items-end bg-primary" :class="searchActive && 'search-active'">
|
||||||
<div
|
<div
|
||||||
class="mx-auto flex w-full max-w-app items-center py-2.5 text-white"
|
class="mx-auto flex w-full max-w-app items-center py-2.5 text-white"
|
||||||
:style="{ height: `${props.height}px` }"
|
:style="{ height: `${props.height}px` }"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="search-active-hide flex items-center pl-3"
|
class="search-active-hide flex items-center pl-3"
|
||||||
:class="sideBarCollapsed ? 'w-fit' : 'max-sm:w-fit md:w-sidebar md:pr-3'"
|
:class="sideBarCollapsed ? 'w-fit' : 'max-sm:w-fit md:w-sidebar md:pr-3'"
|
||||||
>
|
>
|
||||||
<Hamburger
|
<Hamburger
|
||||||
:side-bar-collapsed="props.sideBarCollapsed"
|
:side-bar-collapsed="props.sideBarCollapsed"
|
||||||
@toggle-side-bar="emit('toggleSideBar')"
|
@toggle-side-bar="emit('toggleSideBar')"
|
||||||
/>
|
/>
|
||||||
<Logo
|
<Logo
|
||||||
class="ml-auto pl-5 text-2xl hover:drop-shadow"
|
class="ml-auto pl-5 text-2xl hover:drop-shadow"
|
||||||
id="logo"
|
id="logo"
|
||||||
@click="setActiveNote(rootNote?.id)"
|
@click="setActiveNote(rootNote?.id)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex h-full flex-grow flex-row items-center gap-2 pl-5 pr-3">
|
<div class="flex h-full flex-grow flex-row items-center gap-2 pl-5 pr-3">
|
||||||
<template v-if="!loading">
|
<template v-if="!loading">
|
||||||
<SearchBar @active="(active) => (searchActive = active)" />
|
<SearchBar @active="(active) => (searchActive = active)" />
|
||||||
<UIButton
|
<UIButton
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="search-active-hide topbar-button text-white"
|
class="search-active-hide topbar-button text-white"
|
||||||
@click="addNote('Untitled new note', '', true)"
|
@click="addNote('Untitled new note', '', true)"
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-solid fa-plus-circle scale-[115%]" />
|
<i class="fa-fw fa-solid fa-plus-circle scale-[115%]" />
|
||||||
</UIButton>
|
</UIButton>
|
||||||
<UIModal v-if="initialized && !user">
|
<UIModal v-if="(initialized && !user)">
|
||||||
<template #activator="{ open }">
|
<template #activator="{ open }">
|
||||||
<UIButton
|
<UIButton
|
||||||
size="sm"
|
size="sm"
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="search-active-hide topbar-button py-1 text-white"
|
class="search-active-hide topbar-button py-1 text-white"
|
||||||
@click="open"
|
@click="open"
|
||||||
>
|
>
|
||||||
Sign in
|
Sign in
|
||||||
</UIButton>
|
</UIButton>
|
||||||
</template>
|
</template>
|
||||||
<template #title>Sign in</template>
|
<template #title>Sign in</template>
|
||||||
<template #default="{ close }">
|
<template #default="{ close }">
|
||||||
<Auth @signedIn="handleSignIn(close)" />
|
<Auth @signedIn="handleSignIn(close)" />
|
||||||
</template>
|
</template>
|
||||||
<template #actions="{ close }">
|
<template #actions="{ close }">
|
||||||
<UIButton size="sm" @click="close">Close</UIButton>
|
<UIButton size="sm" @click="close">Close</UIButton>
|
||||||
</template>
|
</template>
|
||||||
</UIModal>
|
</UIModal>
|
||||||
<Settings v-else-if="user" />
|
<Settings v-else-if="user" />
|
||||||
</template>
|
</template>
|
||||||
<SkeletonTopBar v-else />
|
<SkeletonTopBar v-else />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
#logo {
|
#logo {
|
||||||
@apply cursor-pointer transition-all duration-200 active:text-primary;
|
@apply cursor-pointer transition-all duration-200 hover:text-primary;
|
||||||
}
|
|
||||||
@media (hover: hover) and (pointer: fine) {
|
|
||||||
#logo:hover {
|
|
||||||
text-shadow: 0 0 5px white, 0 0 10px white, 0 0 15px white;
|
|
||||||
@apply text-primary;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// @media (hover: hover) and (pointer: fine) {
|
||||||
|
// #logo:hover {
|
||||||
|
// text-shadow: 0 0 5px white, 0 0 10px white, 0 0 15px white;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
#logo:active {
|
#logo:active {
|
||||||
text-shadow: 0 0 5px white, 0 0 10px white, 0 0 15px white;
|
text-shadow: 0 0 5px white, 0 0 10px white, 0 0 15px white;
|
||||||
}
|
}
|
||||||
.topbar-button {
|
.topbar-button {
|
||||||
&:active {
|
&:active {
|
||||||
@apply border-white bg-white text-primary;
|
@apply border-white bg-white text-primary
|
||||||
}
|
}
|
||||||
@apply hover:border-white hover:bg-white hover:text-primary focus-visible:outline-white;
|
@apply hover:border-white hover:bg-white hover:text-primary focus-visible:outline-white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.search-active {
|
.search-active {
|
||||||
.search-active-hide {
|
.search-active-hide {
|
||||||
@apply max-sm:hidden;
|
@apply max-sm:hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,61 +1,61 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
sideBarCollapsed: boolean
|
sideBarCollapsed: boolean
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
toggleSideBar: []
|
toggleSideBar: []
|
||||||
}>()
|
}>()
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<label
|
<label
|
||||||
class="dui-btn-ghost dui-btn dui-btn-sm dui-btn-circle relative inline-grid cursor-pointer select-none place-content-center"
|
class="dui-btn-ghost dui-btn-sm dui-btn-circle dui-btn relative inline-grid cursor-pointer select-none place-content-center"
|
||||||
|
>
|
||||||
|
<input type="checkbox" @click="emit('toggleSideBar')" :checked="!props.sideBarCollapsed" />
|
||||||
|
<svg
|
||||||
|
class="swap-off fill-current"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="24"
|
||||||
|
height="24"
|
||||||
|
viewBox="0 0 512 512"
|
||||||
>
|
>
|
||||||
<input type="checkbox" @click="emit('toggleSideBar')" :checked="!props.sideBarCollapsed" />
|
<path d="M64,384H448V341.33H64Zm0-106.67H448V234.67H64ZM64,128v42.67H448V128Z" />
|
||||||
<svg
|
</svg>
|
||||||
class="swap-off fill-current"
|
<svg
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
class="swap-on fill-current"
|
||||||
width="24"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
height="24"
|
width="24"
|
||||||
viewBox="0 0 512 512"
|
height="24"
|
||||||
>
|
viewBox="0 0 512 512"
|
||||||
<path d="M64,384H448V341.33H64Zm0-106.67H448V234.67H64ZM64,128v42.67H448V128Z" />
|
>
|
||||||
</svg>
|
<polygon
|
||||||
<svg
|
points="400 145.49 366.51 112 256 222.51 145.49 112 112 145.49 222.51 256 112 366.51 145.49 400 256 289.49 366.51 400 400 366.51 289.49 256 400 145.49"
|
||||||
class="swap-on fill-current"
|
/>
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
</svg>
|
||||||
width="24"
|
</label>
|
||||||
height="24"
|
|
||||||
viewBox="0 0 512 512"
|
|
||||||
>
|
|
||||||
<polygon
|
|
||||||
points="400 145.49 366.51 112 256 222.51 145.49 112 112 145.49 222.51 256 112 366.51 145.49 400 256 289.49 366.51 400 400 366.51 289.49 256 400 145.49"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</label>
|
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
input {
|
input {
|
||||||
appearance: none;
|
appearance: none;
|
||||||
}
|
}
|
||||||
label > * {
|
label > * {
|
||||||
transition-duration: 0.2s;
|
transition-duration: 0.2s;
|
||||||
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
transition-property: transform, opacity;
|
transition-property: transform, opacity;
|
||||||
grid-column-start: 1;
|
grid-column-start: 1;
|
||||||
grid-row-start: 1;
|
grid-row-start: 1;
|
||||||
}
|
}
|
||||||
input:checked ~ .swap-off {
|
input:checked ~ .swap-off {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: rotate(-45deg);
|
transform: rotate(-45deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
input:checked ~ .swap-on {
|
input:checked ~ .swap-on {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: rotate(0deg);
|
transform: rotate(0deg);
|
||||||
}
|
}
|
||||||
.swap-on {
|
.swap-on {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: rotate(45deg);
|
transform: rotate(45deg);
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<template>
|
<template>
|
||||||
<i class="fa-brands fa-staylinked"></i>
|
<i class="fa-brands fa-staylinked"></i>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,34 +1,27 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { OnClickOutside } from '@vueuse/components'
|
import { OnClickOutside } from '@vueuse/components'
|
||||||
import { vibrate } from '@/composables/useHaptics'
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<OnClickOutside>
|
<OnClickOutside>
|
||||||
<UIDropdown class="search-active-hide">
|
<UIDropdown class="search-active-hide">
|
||||||
<template #activator>
|
<template #activator>
|
||||||
<UIButton
|
<UIButton :dropdown="true" size="sm" variant="outline" class="topbar-button text-white">
|
||||||
dropdown
|
<i class="fa-fw fa-solid fa-user-gear" />
|
||||||
size="sm"
|
</UIButton>
|
||||||
variant="outline"
|
</template>
|
||||||
class="topbar-button text-white"
|
<template #items>
|
||||||
@mousedown="vibrate"
|
<NotesSourceSwitcher />
|
||||||
>
|
<AccountSettings />
|
||||||
<i class="fa-fw fa-solid fa-user-gear" />
|
<SignOut />
|
||||||
</UIButton>
|
</template>
|
||||||
</template>
|
</UIDropdown>
|
||||||
<template #items>
|
</OnClickOutside>
|
||||||
<NotesSourceSwitcher />
|
|
||||||
<AccountSettings />
|
|
||||||
<SignOut />
|
|
||||||
</template>
|
|
||||||
</UIDropdown>
|
|
||||||
</OnClickOutside>
|
|
||||||
</template>
|
</template>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
.topbar-button {
|
.topbar-button {
|
||||||
&:focus-within {
|
&:focus-within {
|
||||||
@apply border-white bg-white text-primary;
|
@apply border-white bg-white text-primary;
|
||||||
}
|
}
|
||||||
@apply hover:border-white hover:bg-white hover:text-primary focus-visible:outline-white;
|
@apply hover:border-white hover:bg-white hover:text-primary focus-visible:outline-white;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -9,29 +9,29 @@ import FileSaver from 'file-saver'
|
|||||||
|
|
||||||
const verificationEmailSent = ref(false)
|
const verificationEmailSent = ref(false)
|
||||||
const sendVerificationMail = () => {
|
const sendVerificationMail = () => {
|
||||||
if (!user.value) throw Error("User doesn't exist, can't send verification email")
|
if (!user.value) throw Error("User doesn't exist, can't send verification email")
|
||||||
sendEmailVerification(user.value)
|
sendEmailVerification(user.value)
|
||||||
verificationEmailSent.value = true
|
verificationEmailSent.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
const exportNotes = async () => {
|
const exportNotes = async () => {
|
||||||
const zip = new JSZip()
|
const zip = new JSZip()
|
||||||
notes.value.forEach((note) => {
|
notes.value.forEach((note) => {
|
||||||
zip.file(`${note.title}-${note.id}.md`, note.content)
|
zip.file(`${note.title}-${note.id}.md`, note.content)
|
||||||
})
|
})
|
||||||
const blob = await zip.generateAsync({ type: 'blob' })
|
const blob = await zip.generateAsync({ type: 'blob' })
|
||||||
const currentDate = format(new Date(), 'yyyyMMdd')
|
const currentDate = format(new Date(), 'yyyyMMdd')
|
||||||
FileSaver.saveAs(blob, `contexted-${user.value?.email}-${currentDate}.zip`)
|
FileSaver.saveAs(blob, `contexted-${user.value?.email}-${currentDate}.zip`)
|
||||||
}
|
}
|
||||||
|
|
||||||
const showDeleteAccountDialog = ref(false)
|
const showDeleteAccountDialog = ref(false)
|
||||||
const deleteAccount = async () => {
|
const deleteAccount = async () => {
|
||||||
await user.value?.delete()
|
await user.value?.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
const showEncryptionDialog = ref(false)
|
const showEncryptionDialog = ref(false)
|
||||||
watch(showEncryptionDialog, () => {
|
watch(showEncryptionDialog, () => {
|
||||||
passphrase.value = ''
|
passphrase.value = ''
|
||||||
})
|
})
|
||||||
const passphrase = ref('')
|
const passphrase = ref('')
|
||||||
const toggleEncryptionError = ref('')
|
const toggleEncryptionError = ref('')
|
||||||
@@ -39,205 +39,169 @@ const toggleEncryptionError = ref('')
|
|||||||
const encryptionEnabled = computed(() => Boolean(encryptionKey.value))
|
const encryptionEnabled = computed(() => Boolean(encryptionKey.value))
|
||||||
|
|
||||||
const toggleEncryption = async () => {
|
const toggleEncryption = async () => {
|
||||||
const result = encryptionEnabled.value
|
const result = encryptionEnabled.value
|
||||||
? await disableEncryption(passphrase.value)
|
? await disableEncryption(passphrase.value)
|
||||||
: await enableEncryption(passphrase.value)
|
: await enableEncryption(passphrase.value)
|
||||||
if (typeof result === 'string') {
|
if (typeof result === 'string') {
|
||||||
toggleEncryptionError.value = result
|
toggleEncryptionError.value = result
|
||||||
} else {
|
} else {
|
||||||
toggleEncryptionError.value = ''
|
toggleEncryptionError.value = ''
|
||||||
showEncryptionDialog.value = false
|
showEncryptionDialog.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<UIModal size="lg">
|
<UIModal size="lg">
|
||||||
<template #activator="{ open }">
|
<template #activator="{ open }">
|
||||||
<UIDropdownItem @click="open">
|
<UIDropdownItem @click="open">
|
||||||
<i class="fa-fw fa-solid fa-sliders" />
|
<i class="fa-fw fa-solid fa-sliders" />
|
||||||
Account settings
|
Account settings
|
||||||
</UIDropdownItem>
|
</UIDropdownItem>
|
||||||
</template>
|
</template>
|
||||||
<template #title>
|
<template #title>
|
||||||
<i class="fa-fw fa-solid fa-sliders mr-2" />
|
<i class="fa-fw fa-solid fa-sliders mr-2" />
|
||||||
Account settings
|
Account settings
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UICard>
|
<UICard>
|
||||||
<template #title>Account</template>
|
<template #title>Account</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<template v-if="user?.email">
|
<template v-if="user?.email">
|
||||||
<div class="w-full flex-row sm:flex">
|
<div class="w-full flex-row sm:flex">
|
||||||
<div class="font-bold sm:w-4/12">E-mail address</div>
|
<div class="font-bold sm:w-4/12">E-mail address</div>
|
||||||
<div>{{ user.email }}</div>
|
<div>{{ user.email }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="w-full flex-row sm:flex">
|
<div class="w-full flex-row sm:flex">
|
||||||
<div class="font-bold sm:w-4/12">Account status</div>
|
<div class="font-bold sm:w-4/12">Account status</div>
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<UIBadge :color="user.emailVerified ? 'success' : 'warning'">
|
<UIBadge :color="user.emailVerified ? 'success' : 'warning'">
|
||||||
{{ user.emailVerified ? 'Verified' : 'Not yet verified' }}
|
{{ user.emailVerified ? 'Verified' : 'Not yet verified' }}
|
||||||
</UIBadge>
|
</UIBadge>
|
||||||
<UIButton
|
<UIButton
|
||||||
size="sm"
|
size="sm"
|
||||||
class="ml-2"
|
class="ml-2"
|
||||||
@click="sendVerificationMail"
|
@click="sendVerificationMail"
|
||||||
v-if="!user.emailVerified"
|
v-if="!user.emailVerified"
|
||||||
:disabled="verificationEmailSent"
|
:disabled="verificationEmailSent"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
verificationEmailSent
|
verificationEmailSent
|
||||||
? 'Verification email sent'
|
? 'Verification email sent'
|
||||||
: 'Request new verification email'
|
: 'Request new verification email'
|
||||||
}}
|
}}
|
||||||
</UIButton>
|
</UIButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="w-full flex-row sm:flex">
|
<div class="w-full flex-row sm:flex">
|
||||||
<div class="font-bold sm:w-4/12">Account creation date</div>
|
<div class="font-bold sm:w-4/12">Account creation date</div>
|
||||||
<div>
|
<div>
|
||||||
{{
|
{{ format(Date.parse(user?.metadata?.creationTime || ''), 'dd/MM/yyyy') }}
|
||||||
format(
|
</div>
|
||||||
Date.parse(user?.metadata?.creationTime || ''),
|
|
||||||
'dd/MM/yyyy'
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</UICard>
|
|
||||||
<UICard>
|
|
||||||
<template #title>Notes</template>
|
|
||||||
<template #default>
|
|
||||||
<div class="items-top w-full flex-row sm:flex">
|
|
||||||
<div class="font-bold sm:w-4/12">Export notes</div>
|
|
||||||
<UIButton size="sm" @click="exportNotes">
|
|
||||||
<i class="fa-fw fa-solid fa-file-export mr-2"></i>
|
|
||||||
Export notes
|
|
||||||
</UIButton>
|
|
||||||
</div>
|
|
||||||
<div class="items-top w-full flex-row sm:flex sm:flex-grow">
|
|
||||||
<div class="flex-shrink-0 font-bold sm:w-4/12">Delete account</div>
|
|
||||||
<div>
|
|
||||||
<UIButton
|
|
||||||
size="sm"
|
|
||||||
color="error"
|
|
||||||
@click="showDeleteAccountDialog = true"
|
|
||||||
>
|
|
||||||
<i class="fa-fw fa-solid fa-trash mr-2"></i>
|
|
||||||
Delete account
|
|
||||||
</UIButton>
|
|
||||||
<UIAlert
|
|
||||||
color="warning"
|
|
||||||
density="compact"
|
|
||||||
class="mt-1 space-y-2 text-sm"
|
|
||||||
v-if="showDeleteAccountDialog"
|
|
||||||
>
|
|
||||||
<div>
|
|
||||||
Are you sure you want to delete your Contexted account? This
|
|
||||||
action cannot be undone!
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-wrap gap-2">
|
|
||||||
<UIButton
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
color="primary"
|
|
||||||
@click="deleteAccount"
|
|
||||||
>
|
|
||||||
Delete account
|
|
||||||
</UIButton>
|
|
||||||
<UIButton
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
@click="showDeleteAccountDialog = false"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</UIButton>
|
|
||||||
</div>
|
|
||||||
</UIAlert>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="items-top w-full flex-row sm:flex">
|
|
||||||
<div class="flex-shrink-0 font-bold sm:w-4/12">
|
|
||||||
End-to-end encryption
|
|
||||||
</div>
|
|
||||||
<div class="w-full">
|
|
||||||
<template v-if="!encryptionEnabled">
|
|
||||||
<UIButton
|
|
||||||
size="sm"
|
|
||||||
@click="showEncryptionDialog = true"
|
|
||||||
v-if="showEncryptionDialog === false"
|
|
||||||
>
|
|
||||||
<i class="fa-fw fa-solid fa-key"></i>
|
|
||||||
Enable end-to-end encryption
|
|
||||||
</UIButton>
|
|
||||||
</template>
|
|
||||||
<template v-else>
|
|
||||||
<UIButton
|
|
||||||
size="sm"
|
|
||||||
@click="showEncryptionDialog = true"
|
|
||||||
v-if="showEncryptionDialog === false"
|
|
||||||
>
|
|
||||||
<i class="fa-fw fa-solid fa-key mr-2"></i>
|
|
||||||
Disable end-to-end encryption
|
|
||||||
</UIButton>
|
|
||||||
</template>
|
|
||||||
<UIAlert
|
|
||||||
color="info"
|
|
||||||
density="compact"
|
|
||||||
class="text-sm"
|
|
||||||
v-if="showEncryptionDialog"
|
|
||||||
>
|
|
||||||
<div class="w-full space-y-2">
|
|
||||||
<div>
|
|
||||||
Enter your passphrase to
|
|
||||||
{{ encryptionEnabled ? 'disable' : 'enable' }}
|
|
||||||
encryption
|
|
||||||
</div>
|
|
||||||
<UIInputText
|
|
||||||
size="sm"
|
|
||||||
type="password"
|
|
||||||
:color="toggleEncryptionError ? 'error' : 'regular'"
|
|
||||||
v-model="passphrase"
|
|
||||||
class="w-full !max-w-full"
|
|
||||||
/>
|
|
||||||
<UIAlert
|
|
||||||
density="compact"
|
|
||||||
color="error"
|
|
||||||
v-if="toggleEncryptionError"
|
|
||||||
>
|
|
||||||
<i class="fa-solid fa-triangle-exclamation"></i>
|
|
||||||
{{ toggleEncryptionError }}
|
|
||||||
</UIAlert>
|
|
||||||
<div class="flex flex-wrap gap-2">
|
|
||||||
<UIButton
|
|
||||||
:disabled="passphrase.length === 0"
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
color="primary"
|
|
||||||
@click="toggleEncryption"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
encryptionEnabled ? 'Disable' : 'Enable'
|
|
||||||
}}
|
|
||||||
encryption
|
|
||||||
</UIButton>
|
|
||||||
<UIButton
|
|
||||||
size="sm"
|
|
||||||
variant="outline"
|
|
||||||
@click="showEncryptionDialog = false"
|
|
||||||
>
|
|
||||||
Cancel
|
|
||||||
</UIButton>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</UIAlert>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</UICard>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</UIModal>
|
</UICard>
|
||||||
|
<UICard>
|
||||||
|
<template #title>Notes</template>
|
||||||
|
<template #default>
|
||||||
|
<div class="items-top w-full flex-row sm:flex">
|
||||||
|
<div class="font-bold sm:w-4/12">Export notes</div>
|
||||||
|
<UIButton size="sm" @click="exportNotes">
|
||||||
|
<i class="fa-fw fa-solid fa-file-export"></i>
|
||||||
|
Export notes
|
||||||
|
</UIButton>
|
||||||
|
</div>
|
||||||
|
<div class="items-top w-full flex-row sm:flex sm:flex-grow">
|
||||||
|
<div class="flex-shrink-0 font-bold sm:w-4/12">Delete account</div>
|
||||||
|
<div>
|
||||||
|
<UIButton size="sm" color="error" @click="showDeleteAccountDialog = true">
|
||||||
|
<i class="fa-fw fa-solid fa-trash"></i>
|
||||||
|
Delete account
|
||||||
|
</UIButton>
|
||||||
|
<UIAlert
|
||||||
|
color="warning"
|
||||||
|
density="compact"
|
||||||
|
class="mt-1 space-y-2 text-sm"
|
||||||
|
v-if="showDeleteAccountDialog"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
Are you sure you want to delete your Contexted account? This action cannot be
|
||||||
|
undone!
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<UIButton size="sm" variant="outline" color="primary" @click="deleteAccount">
|
||||||
|
Delete account
|
||||||
|
</UIButton>
|
||||||
|
<UIButton size="sm" variant="outline" @click="showDeleteAccountDialog = false">
|
||||||
|
Cancel
|
||||||
|
</UIButton>
|
||||||
|
</div>
|
||||||
|
</UIAlert>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="items-top w-full flex-row sm:flex">
|
||||||
|
<div class="flex-shrink-0 font-bold sm:w-4/12">End-to-end encryption</div>
|
||||||
|
<div class="w-full">
|
||||||
|
<template v-if="!encryptionEnabled">
|
||||||
|
<UIButton
|
||||||
|
size="sm"
|
||||||
|
@click="showEncryptionDialog = true"
|
||||||
|
v-if="showEncryptionDialog === false"
|
||||||
|
>
|
||||||
|
<i class="fa-fw fa-solid fa-key"></i>
|
||||||
|
Enable end-to-end encryption
|
||||||
|
</UIButton>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<UIButton
|
||||||
|
size="sm"
|
||||||
|
@click="showEncryptionDialog = true"
|
||||||
|
v-if="showEncryptionDialog === false"
|
||||||
|
>
|
||||||
|
<i class="fa-fw fa-solid fa-key"></i>
|
||||||
|
Disable end-to-end encryption
|
||||||
|
</UIButton>
|
||||||
|
</template>
|
||||||
|
<UIAlert color="info" density="compact" class="text-sm" v-if="showEncryptionDialog">
|
||||||
|
<div class="w-full space-y-2">
|
||||||
|
<div>
|
||||||
|
Enter your passphrase to
|
||||||
|
{{ encryptionEnabled ? 'disable' : 'enable' }}
|
||||||
|
encryption
|
||||||
|
</div>
|
||||||
|
<UIInputText
|
||||||
|
size="sm"
|
||||||
|
type="password"
|
||||||
|
:color="toggleEncryptionError ? 'error' : 'regular'"
|
||||||
|
v-model="passphrase"
|
||||||
|
class="w-full !max-w-full"
|
||||||
|
/>
|
||||||
|
<UIAlert density="compact" color="error" v-if="toggleEncryptionError">
|
||||||
|
<i class="fa-solid fa-triangle-exclamation"></i>
|
||||||
|
{{ toggleEncryptionError }}
|
||||||
|
</UIAlert>
|
||||||
|
<div class="flex flex-wrap gap-2">
|
||||||
|
<UIButton
|
||||||
|
:disabled="passphrase.length === 0"
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
color="primary"
|
||||||
|
@click="toggleEncryption"
|
||||||
|
>
|
||||||
|
{{ encryptionEnabled ? 'Disable' : 'Enable' }} encryption
|
||||||
|
</UIButton>
|
||||||
|
<UIButton size="sm" variant="outline" @click="showEncryptionDialog = false">
|
||||||
|
Cancel
|
||||||
|
</UIButton>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</UIAlert>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UICard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</UIModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,24 +3,24 @@ import { activeNotesSource, availableNotesSources } from '@/composables/useNotes
|
|||||||
import { preferredNotesSource } from '@/composables/useSettings'
|
import { preferredNotesSource } from '@/composables/useSettings'
|
||||||
|
|
||||||
const sourceLabels: { [source: string]: string } = {
|
const sourceLabels: { [source: string]: string } = {
|
||||||
local: 'Switch to local notes',
|
local: 'Switch to local notes',
|
||||||
firebase: 'Switch to cloud notes'
|
firebase: 'Switch to cloud notes'
|
||||||
}
|
}
|
||||||
|
|
||||||
const blur = () => (document.activeElement as HTMLElement)?.blur()
|
const blur = () => (document.activeElement as HTMLElement)?.blur()
|
||||||
|
|
||||||
const handleClick = (fn: (...args: any[]) => any) => {
|
const handleClick = (fn: (...args: any[]) => any) => {
|
||||||
blur()
|
blur()
|
||||||
fn()
|
fn()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<UIDropdownItem
|
<UIDropdownItem
|
||||||
v-for="source in availableNotesSources.filter((source) => source !== activeNotesSource)"
|
v-for="source in availableNotesSources.filter((source) => source !== activeNotesSource)"
|
||||||
:key="source"
|
:key="source"
|
||||||
@click="handleClick(() => (preferredNotesSource = source))"
|
@click="handleClick(() => (preferredNotesSource = source))"
|
||||||
>
|
>
|
||||||
<i class="fa-fw fa-solid fa-database" />
|
<i class="fa-fw fa-solid fa-database" />
|
||||||
{{ sourceLabels[source] }}
|
{{ sourceLabels[source] }}
|
||||||
</UIDropdownItem>
|
</UIDropdownItem>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,33 +3,33 @@ import { preferredNotesSource } from '@/composables/useSettings'
|
|||||||
import { signOut as firebaseSignOut } from '@/composables/useFirebase'
|
import { signOut as firebaseSignOut } from '@/composables/useFirebase'
|
||||||
import { clearEncryptionKeys } from '@/composables/useEncryption'
|
import { clearEncryptionKeys } from '@/composables/useEncryption'
|
||||||
|
|
||||||
const signOut = async (close: () => Promise<void>) => {
|
const signOut = async (close: () => Promise<boolean>) => {
|
||||||
await close()
|
await close()
|
||||||
await firebaseSignOut()
|
await firebaseSignOut()
|
||||||
preferredNotesSource.value = null
|
preferredNotesSource.value = null
|
||||||
clearEncryptionKeys()
|
clearEncryptionKeys()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<UIModal>
|
<UIModal>
|
||||||
<template #activator="{ open }">
|
<template #activator="{ open }">
|
||||||
<UIDropdownItem @click="open">
|
<UIDropdownItem @click="open">
|
||||||
<i class="fa-fw fa-solid fa-right-from-bracket" />
|
<i class="fa-fw fa-solid fa-right-from-bracket" />
|
||||||
Sign out
|
Sign out
|
||||||
</UIDropdownItem>
|
</UIDropdownItem>
|
||||||
</template>
|
</template>
|
||||||
<template #title>
|
<template #title>
|
||||||
<i class="fa-fw fa-solid fa-right-from-bracket mr-2" />
|
<i class="fa-fw fa-solid fa-right-from-bracket mr-2" />
|
||||||
Sign out
|
Sign out
|
||||||
</template>
|
</template>
|
||||||
<template #default>
|
<template #default>
|
||||||
<p>Are you sure want to signout?</p>
|
<p>Are you sure want to signout?</p>
|
||||||
<p>Your synchronized notes can't be accessed until you sign-in again.</p>
|
<p>Your synchronized notes can't be accessed until you sign-in again.</p>
|
||||||
</template>
|
</template>
|
||||||
<template #actions="{ close }">
|
<template #actions="{ close }">
|
||||||
<UIButton size="sm" @click="close">Cancel</UIButton>
|
<UIButton size="sm" @click="close">Cancel</UIButton>
|
||||||
<UIButton size="sm" color="primary" @click="signOut(close)">Sign out</UIButton>
|
<UIButton size="sm" color="primary" @click="signOut(close)">Sign out</UIButton>
|
||||||
</template>
|
</template>
|
||||||
</UIModal>
|
</UIModal>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,108 +3,105 @@ import { getNoteReferences, setActiveNote, findNotes, deleteNote } from '@/compo
|
|||||||
import { formatDate } from '@/utils/helpers'
|
import { formatDate } from '@/utils/helpers'
|
||||||
|
|
||||||
const notesWithReferences = computed(() => {
|
const notesWithReferences = computed(() => {
|
||||||
return findNotes(filter.value).map((note) => ({
|
return findNotes(filter.value).map((note) => ({
|
||||||
...note,
|
...note,
|
||||||
references: getNoteReferences(note)
|
references: getNoteReferences(note)
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
|
|
||||||
const selectedNotes = ref<{ [key: string]: Boolean }>({})
|
const selectedNotes = ref<{ [key: string]: Boolean }>({})
|
||||||
const countSelectedNotes = computed<number>(
|
const countSelectedNotes = computed<number>(
|
||||||
() => Object.entries(selectedNotes.value).filter(([, selected]) => Boolean(selected)).length
|
() => Object.entries(selectedNotes.value).filter(([, selected]) => Boolean(selected)).length
|
||||||
)
|
)
|
||||||
|
|
||||||
const toggleRow = (note: Note) => {
|
const toggleRow = (note: Note) => {
|
||||||
if (!note.isRoot) selectedNotes.value[note.id] = !selectedNotes.value[note.id]
|
if (!note.isRoot) selectedNotes.value[note.id] = !selectedNotes.value[note.id]
|
||||||
}
|
}
|
||||||
|
|
||||||
const filter = ref<string>('')
|
const filter = ref<string>('')
|
||||||
|
|
||||||
const deleteSelectedNotes = (closeModal: () => void) => {
|
const deleteSelectedNotes = (closeModal: () => void) => {
|
||||||
closeModal()
|
closeModal()
|
||||||
const notesToDelete = Object.entries(selectedNotes.value)
|
const notesToDelete = Object.entries(selectedNotes.value)
|
||||||
.filter(([, selected]) => Boolean(selected))
|
.filter(([, selected]) => Boolean(selected))
|
||||||
.map(([id]) => id)
|
.map(([id]) => id)
|
||||||
notesToDelete.forEach((noteId) => deleteNote(noteId))
|
notesToDelete.forEach((noteId) => deleteNote(noteId))
|
||||||
selectedNotes.value = {}
|
selectedNotes.value = {}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex w-full flex-col gap-2">
|
<div class="flex w-full flex-col gap-2">
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="whitespace-nowrap">
|
<span class="whitespace-nowrap">
|
||||||
{{ notesWithReferences.length }}
|
{{ notesWithReferences.length }} {{ notesWithReferences.length === 1 ? 'note' : 'notes' }}
|
||||||
{{ notesWithReferences.length === 1 ? 'note' : 'notes' }}
|
</span>
|
||||||
</span>
|
<template v-if="countSelectedNotes > 0">
|
||||||
<template v-if="countSelectedNotes > 0">
|
<span class="mx-1">|</span>
|
||||||
<span class="mx-1">|</span>
|
<div class="whitespace-nowrap font-semibold">{{ countSelectedNotes }} selected</div>
|
||||||
<div class="whitespace-nowrap font-semibold">
|
</template>
|
||||||
{{ countSelectedNotes }} selected
|
</div>
|
||||||
</div>
|
<UIModal v-if="countSelectedNotes > 0">
|
||||||
</template>
|
<template #activator="{ open }">
|
||||||
</div>
|
<UIButton size="sm" @click="open">Delete</UIButton>
|
||||||
<UIModal v-if="countSelectedNotes > 0">
|
</template>
|
||||||
<template #activator="{ open }">
|
<template #default>Are you sure you want to delete the selected notes?</template>
|
||||||
<UIButton size="sm" @click="open">Delete</UIButton>
|
<template #actions="{ close }">
|
||||||
</template>
|
<UIButton size="sm" color="primary" @click="deleteSelectedNotes(close)">
|
||||||
<template #default>Are you sure you want to delete the selected notes?</template>
|
Delete selected notes
|
||||||
<template #actions="{ close }">
|
</UIButton>
|
||||||
<UIButton size="sm" color="primary" @click="deleteSelectedNotes(close)">
|
<UIButton size="sm" @click="close">Close</UIButton>
|
||||||
Delete selected notes
|
</template>
|
||||||
</UIButton>
|
</UIModal>
|
||||||
<UIButton size="sm" @click="close">Close</UIButton>
|
<UIInputText
|
||||||
</template>
|
size="sm"
|
||||||
</UIModal>
|
v-model="filter"
|
||||||
<UIInputText
|
placeholder="Start typing to filter"
|
||||||
size="sm"
|
class="my-1 ml-auto mr-1 max-w-xs flex-grow"
|
||||||
v-model="filter"
|
></UIInputText>
|
||||||
placeholder="Start typing to filter"
|
|
||||||
class="my-1 ml-auto mr-1 max-w-xs flex-grow"
|
|
||||||
></UIInputText>
|
|
||||||
</div>
|
|
||||||
<div class="overflow-x-auto">
|
|
||||||
<UITable size="md" class="w-full">
|
|
||||||
<thead>
|
|
||||||
<tr class="bg-base-200 text-sm text-base-content">
|
|
||||||
<th class="w-[48px]"></th>
|
|
||||||
<th>Note title</th>
|
|
||||||
<th class="w-[75px]">Words</th>
|
|
||||||
<th class="w-[100px]">References</th>
|
|
||||||
<th class="w-[150px]">Modified</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr
|
|
||||||
v-for="note in notesWithReferences"
|
|
||||||
:key="note.id"
|
|
||||||
class="dui-hover hover:cursor-pointer"
|
|
||||||
@click="setActiveNote(note.id)"
|
|
||||||
>
|
|
||||||
<th @click.stop="toggleRow(note)" class="text-center">
|
|
||||||
<label>
|
|
||||||
<UIInputCheckbox
|
|
||||||
color="primary"
|
|
||||||
:checked="Boolean(selectedNotes[note.id])"
|
|
||||||
:disabled="note.isRoot"
|
|
||||||
></UIInputCheckbox>
|
|
||||||
</label>
|
|
||||||
</th>
|
|
||||||
<td>
|
|
||||||
<i class="fas fa-fw fa-home mr-1 text-secondary" v-if="note.isRoot" />
|
|
||||||
{{ note.title }}
|
|
||||||
</td>
|
|
||||||
<td>{{ note.wordCount }}</td>
|
|
||||||
<td>
|
|
||||||
<UIBadge v-if="note.references.length > 0">
|
|
||||||
<i data-v-41bbc26f="" class="fas fa-fw fa-sign-out-alt mr-1"></i>
|
|
||||||
{{ note.references.length }}
|
|
||||||
</UIBadge>
|
|
||||||
</td>
|
|
||||||
<td>{{ formatDate(note.modified) }}</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</UITable>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
<div class="overflow-x-auto">
|
||||||
|
<UITable size="md" class="w-full">
|
||||||
|
<thead>
|
||||||
|
<tr class="bg-base-200 text-sm text-base-content">
|
||||||
|
<th class="w-[48px]"></th>
|
||||||
|
<th>Note title</th>
|
||||||
|
<th class="w-[75px]">Words</th>
|
||||||
|
<th class="w-[100px]">References</th>
|
||||||
|
<th class="w-[150px]">Modified</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr
|
||||||
|
v-for="note in notesWithReferences"
|
||||||
|
:key="note.id"
|
||||||
|
class="dui-hover hover:cursor-pointer"
|
||||||
|
@click="setActiveNote(note.id)"
|
||||||
|
>
|
||||||
|
<th @click.stop="toggleRow(note)" class="text-center">
|
||||||
|
<label>
|
||||||
|
<UIInputCheckbox
|
||||||
|
color="primary"
|
||||||
|
:checked="Boolean(selectedNotes[note.id])"
|
||||||
|
:disabled="note.isRoot"
|
||||||
|
></UIInputCheckbox>
|
||||||
|
</label>
|
||||||
|
</th>
|
||||||
|
<td>
|
||||||
|
<i class="fas fa-fw fa-home mr-1 text-secondary" v-if="note.isRoot" />
|
||||||
|
{{ note.title }}
|
||||||
|
</td>
|
||||||
|
<td>{{ note.wordCount }}</td>
|
||||||
|
<td>
|
||||||
|
<UIBadge v-if="note.references.length > 0">
|
||||||
|
<i data-v-41bbc26f="" class="fas fa-fw fa-sign-out-alt mr-1"></i>
|
||||||
|
{{ note.references.length }}
|
||||||
|
</UIBadge>
|
||||||
|
</td>
|
||||||
|
<td>{{ formatDate(note.modified) }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</UITable>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,303 +1,294 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import {
|
import {
|
||||||
notesRelations,
|
notesRelations,
|
||||||
getNoteById,
|
getNoteById,
|
||||||
activeNote,
|
activeNote,
|
||||||
setActiveNote,
|
setActiveNote,
|
||||||
rootNote
|
rootNote
|
||||||
} from '@/composables/useNotes'
|
} from '@/composables/useNotes'
|
||||||
import cytoscape from 'cytoscape'
|
import cytoscape from 'cytoscape'
|
||||||
import shortid from 'shortid'
|
import shortid from 'shortid'
|
||||||
|
|
||||||
const renderMindmap = () => {
|
const renderMindmap = () => {
|
||||||
const mindmapCanvas = mindmapElement.value
|
const mindmapCanvas = mindmapElement.value
|
||||||
if (!mindmapCanvas) return
|
if (!mindmapCanvas) return
|
||||||
const style = {
|
const style = {
|
||||||
contextedBlue: '#1e4bc4',
|
contextedBlue: '#1e4bc4',
|
||||||
nodeBackground: '#6c757d',
|
nodeBackground: '#6c757d',
|
||||||
edge: '#ced4da'
|
edge: '#ced4da'
|
||||||
}
|
}
|
||||||
|
|
||||||
const boundingBox = {
|
const boundingBox = {
|
||||||
x1: 0,
|
x1: 0,
|
||||||
y1: 0,
|
y1: 0,
|
||||||
w: mindmapCanvas.clientWidth,
|
w: mindmapCanvas.clientWidth,
|
||||||
h: mindmapCanvas.clientHeight
|
h: mindmapCanvas.clientHeight
|
||||||
}
|
}
|
||||||
const elements = {
|
const elements = {
|
||||||
nodes: nodes.value,
|
nodes: nodes.value,
|
||||||
edges: [
|
edges: [
|
||||||
...links.value.map((link) => ({
|
...links.value.map((link) => ({ data: { id: `${link.source}-${link.target}`, ...link } }))
|
||||||
data: { id: `${link.source}-${link.target}`, ...link }
|
]
|
||||||
}))
|
}
|
||||||
]
|
const cy = cytoscape({
|
||||||
}
|
container: mindmapCanvas,
|
||||||
const cy = cytoscape({
|
elements,
|
||||||
container: mindmapCanvas,
|
autoungrabify: true,
|
||||||
elements,
|
autounselectify: true,
|
||||||
autoungrabify: true,
|
layout: {
|
||||||
autounselectify: true,
|
name: 'cose',
|
||||||
layout: {
|
|
||||||
name: 'cose',
|
|
||||||
|
|
||||||
// Called on `layoutready`
|
// Called on `layoutready`
|
||||||
ready: function () {},
|
ready: function () {},
|
||||||
|
|
||||||
// Called on `layoutstop`
|
// Called on `layoutstop`
|
||||||
stop: function () {},
|
stop: function () {},
|
||||||
|
|
||||||
// Whether to animate while running the layout
|
// Whether to animate while running the layout
|
||||||
// true : Animate continuously as the layout is running
|
// true : Animate continuously as the layout is running
|
||||||
// false : Just show the end result
|
// false : Just show the end result
|
||||||
// 'end' : Animate with the end result, from the initial positions to the end positions
|
// 'end' : Animate with the end result, from the initial positions to the end positions
|
||||||
animate: false,
|
animate: false,
|
||||||
|
|
||||||
// Easing of the animation for animate:'end'
|
// Easing of the animation for animate:'end'
|
||||||
animationEasing: undefined,
|
animationEasing: undefined,
|
||||||
|
|
||||||
// The duration of the animation for animate:'end'
|
// The duration of the animation for animate:'end'
|
||||||
animationDuration: undefined,
|
animationDuration: undefined,
|
||||||
|
|
||||||
// A function that determines whether the node should be animated
|
// A function that determines whether the node should be animated
|
||||||
// All nodes animated by default on animate enabled
|
// All nodes animated by default on animate enabled
|
||||||
// Non-animated nodes are positioned immediately when the layout starts
|
// Non-animated nodes are positioned immediately when the layout starts
|
||||||
// animateFilter: function (node, i) {
|
// animateFilter: function (node, i) {
|
||||||
animateFilter: function () {
|
animateFilter: function () {
|
||||||
return true
|
return true
|
||||||
},
|
},
|
||||||
|
|
||||||
// The layout animates only after this many milliseconds for animate:true
|
// The layout animates only after this many milliseconds for animate:true
|
||||||
// (prevents flashing on fast runs)
|
// (prevents flashing on fast runs)
|
||||||
animationThreshold: 250,
|
animationThreshold: 250,
|
||||||
|
|
||||||
// Number of iterations between consecutive screen positions update
|
// Number of iterations between consecutive screen positions update
|
||||||
refresh: 20,
|
refresh: 20,
|
||||||
|
|
||||||
// Whether to fit the network view after when done
|
// Whether to fit the network view after when done
|
||||||
fit: true,
|
fit: true,
|
||||||
|
|
||||||
// Padding on fit
|
// Padding on fit
|
||||||
padding: 30,
|
padding: 30,
|
||||||
|
|
||||||
// Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
|
// Constrain layout bounds; { x1, y1, x2, y2 } or { x1, y1, w, h }
|
||||||
// boundingBox: undefined,
|
// boundingBox: undefined,
|
||||||
boundingBox,
|
boundingBox,
|
||||||
|
|
||||||
// Excludes the label when calculating node bounding boxes for the layout algorithm
|
// Excludes the label when calculating node bounding boxes for the layout algorithm
|
||||||
nodeDimensionsIncludeLabels: false,
|
nodeDimensionsIncludeLabels: false,
|
||||||
|
|
||||||
// Randomize the initial positions of the nodes (true) or use existing positions (false)
|
// Randomize the initial positions of the nodes (true) or use existing positions (false)
|
||||||
randomize: false,
|
randomize: false,
|
||||||
|
|
||||||
// Extra spacing between components in non-compound graphs
|
// Extra spacing between components in non-compound graphs
|
||||||
componentSpacing: 40,
|
componentSpacing: 40,
|
||||||
|
|
||||||
// Node repulsion (non overlapping) multiplier
|
// Node repulsion (non overlapping) multiplier
|
||||||
// nodeRepulsion: function (node) {
|
// nodeRepulsion: function (node) {
|
||||||
nodeRepulsion: function () {
|
nodeRepulsion: function () {
|
||||||
return 2048
|
return 2048
|
||||||
},
|
},
|
||||||
|
|
||||||
// Node repulsion (overlapping) multiplier
|
// Node repulsion (overlapping) multiplier
|
||||||
nodeOverlap: 4,
|
nodeOverlap: 4,
|
||||||
|
|
||||||
// Ideal edge (non nested) length
|
// Ideal edge (non nested) length
|
||||||
// idealEdgeLength: function (edge) {
|
// idealEdgeLength: function (edge) {
|
||||||
idealEdgeLength: function () {
|
idealEdgeLength: function () {
|
||||||
return 32
|
return 32
|
||||||
},
|
},
|
||||||
|
|
||||||
// Divisor to compute edge forces
|
// Divisor to compute edge forces
|
||||||
// edgeElasticity: function (edge) {
|
// edgeElasticity: function (edge) {
|
||||||
edgeElasticity: function () {
|
edgeElasticity: function () {
|
||||||
return 32
|
return 32
|
||||||
},
|
},
|
||||||
|
|
||||||
// Nesting factor (multiplier) to compute ideal edge length for nested edges
|
// Nesting factor (multiplier) to compute ideal edge length for nested edges
|
||||||
nestingFactor: 1.2,
|
nestingFactor: 1.2,
|
||||||
|
|
||||||
// Gravity force (constant)
|
// Gravity force (constant)
|
||||||
gravity: 1,
|
gravity: 1,
|
||||||
|
|
||||||
// Maximum number of iterations to perform
|
// Maximum number of iterations to perform
|
||||||
numIter: 1000,
|
numIter: 1000,
|
||||||
|
|
||||||
// Initial temperature (maximum node displacement)
|
// Initial temperature (maximum node displacement)
|
||||||
initialTemp: 1000,
|
initialTemp: 1000,
|
||||||
|
|
||||||
// Cooling factor (how the temperature is reduced between consecutive iterations
|
// Cooling factor (how the temperature is reduced between consecutive iterations
|
||||||
coolingFactor: 0.99,
|
coolingFactor: 0.99,
|
||||||
|
|
||||||
// Lower temperature threshold (below this point the layout will end)
|
// Lower temperature threshold (below this point the layout will end)
|
||||||
minTemp: 1.0
|
minTemp: 1.0
|
||||||
},
|
},
|
||||||
// userZoomingEnabled: false,
|
// userZoomingEnabled: false,
|
||||||
userPanningEnabled: false,
|
userPanningEnabled: false,
|
||||||
pixelRatio: window.devicePixelRatio ? window.devicePixelRatio * 1.5 : 'auto',
|
pixelRatio: window.devicePixelRatio ? window.devicePixelRatio * 1.5 : 'auto',
|
||||||
style: [
|
style: [
|
||||||
// the stylesheet for the graph
|
// the stylesheet for the graph
|
||||||
{
|
{
|
||||||
selector: 'node',
|
selector: 'node',
|
||||||
style: {
|
style: {
|
||||||
'background-color': style.nodeBackground,
|
'background-color': style.nodeBackground,
|
||||||
label: 'data(title)',
|
label: 'data(title)',
|
||||||
'font-family': 'Source Sans Pro, sans-serif',
|
'font-family': 'Source Sans Pro, sans-serif',
|
||||||
'font-weight': 400,
|
'font-weight': 400,
|
||||||
'font-size': '1.5em',
|
'font-size': '1.5em',
|
||||||
'text-events': 'yes'
|
'text-events': 'yes'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: '.current',
|
selector: '.current',
|
||||||
style: {
|
style: {
|
||||||
'background-color': style.contextedBlue
|
'background-color': style.contextedBlue
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: '.mouseover',
|
selector: '.mouseover',
|
||||||
style: {
|
style: {
|
||||||
'background-color': style.contextedBlue,
|
'background-color': style.contextedBlue,
|
||||||
color: style.contextedBlue
|
color: style.contextedBlue
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: 'edge',
|
selector: 'edge',
|
||||||
style: {
|
style: {
|
||||||
width: 3,
|
width: 3,
|
||||||
'line-color': style.edge,
|
'line-color': style.edge,
|
||||||
'target-arrow-color': style.edge,
|
'target-arrow-color': style.edge,
|
||||||
'target-arrow-shape': 'triangle',
|
'target-arrow-shape': 'triangle',
|
||||||
'curve-style': 'bezier'
|
'curve-style': 'bezier'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
cy.nodes().forEach((node) => {
|
cy.nodes().forEach((node) => {
|
||||||
if (node.data('id') === activeNote.value?.id) node.addClass('current')
|
if (node.data('id') === activeNote.value?.id) node.addClass('current')
|
||||||
})
|
})
|
||||||
cy.nodes().on('tap', (event) => {
|
cy.nodes().on('tap', (event) => {
|
||||||
setActiveNote(event.target.data('id'))
|
setActiveNote(event.target.data('id'))
|
||||||
})
|
})
|
||||||
cy.on('mouseover', 'node', (event) => {
|
cy.on('mouseover', 'node', (event) => {
|
||||||
event.target.addClass('mouseover')
|
event.target.addClass('mouseover')
|
||||||
mindmapCanvas.classList.add('mouseover')
|
mindmapCanvas.classList.add('mouseover')
|
||||||
})
|
})
|
||||||
cy.on('mouseout', 'node', (event) => {
|
cy.on('mouseout', 'node', (event) => {
|
||||||
event.target.removeClass('mouseover')
|
event.target.removeClass('mouseover')
|
||||||
mindmapCanvas.classList.remove('mouseover')
|
mindmapCanvas.classList.remove('mouseover')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const mindmapElement = ref<HTMLInputElement | null>(null)
|
const mindmapElement = ref<HTMLInputElement | null>(null)
|
||||||
|
|
||||||
interface Mindmap {
|
interface Mindmap {
|
||||||
id: string
|
id: string
|
||||||
notes: string[]
|
notes: string[]
|
||||||
isRoot: boolean
|
isRoot: boolean
|
||||||
}
|
}
|
||||||
const selectedMindmap = ref<Mindmap>()
|
const selectedMindmap = ref<Mindmap>()
|
||||||
const mindmaps = computed<Mindmap[]>(() => {
|
const mindmaps = computed<Mindmap[]>(() => {
|
||||||
const mindmaps = Object.entries(notesRelations.value).reduce(
|
const mindmaps = Object.entries(notesRelations.value).reduce((mindmaps, [noteId, relations]) => {
|
||||||
(mindmaps, [noteId, relations]) => {
|
const atomicMindmap = [noteId, ...relations.to, ...relations.from]
|
||||||
const atomicMindmap = [noteId, ...relations.to, ...relations.from]
|
const indices = mindmaps
|
||||||
const indices = mindmaps
|
.filter(
|
||||||
.filter(
|
(mindmap) => [...mindmap].filter((noteId) => atomicMindmap.includes(noteId)).length > 0
|
||||||
(mindmap) =>
|
)
|
||||||
[...mindmap].filter((noteId) => atomicMindmap.includes(noteId)).length > 0
|
.map((mindmap) => mindmaps.indexOf(mindmap))
|
||||||
)
|
if (indices.length > 0) {
|
||||||
.map((mindmap) => mindmaps.indexOf(mindmap))
|
const index = indices[0]
|
||||||
if (indices.length > 0) {
|
const currentMindmap = indices.reduce(
|
||||||
const index = indices[0]
|
(mindmap, index) => mindmap.concat(mindmaps[index]),
|
||||||
const currentMindmap = indices.reduce(
|
[] as string[]
|
||||||
(mindmap, index) => mindmap.concat(mindmaps[index]),
|
)
|
||||||
[] as string[]
|
indices.forEach((index, i) => {
|
||||||
)
|
if (i !== 0) mindmaps.splice(index, 1)
|
||||||
indices.forEach((index, i) => {
|
})
|
||||||
if (i !== 0) mindmaps.splice(index, 1)
|
mindmaps[index] = [...currentMindmap, ...atomicMindmap].filter(
|
||||||
})
|
(item, index, arr) => arr.indexOf(item) === index
|
||||||
mindmaps[index] = [...currentMindmap, ...atomicMindmap].filter(
|
)
|
||||||
(item, index, arr) => arr.indexOf(item) === index
|
} else {
|
||||||
)
|
mindmaps.push(atomicMindmap)
|
||||||
} else {
|
}
|
||||||
mindmaps.push(atomicMindmap)
|
|
||||||
}
|
|
||||||
return mindmaps
|
|
||||||
},
|
|
||||||
[] as string[][]
|
|
||||||
)
|
|
||||||
return mindmaps
|
return mindmaps
|
||||||
.filter((mindmap) => mindmap.length > 1)
|
}, [] as string[][])
|
||||||
.sort((a, b) => b.length - a.length)
|
return mindmaps
|
||||||
.sort((a, b) => {
|
.filter((mindmap) => mindmap.length > 1)
|
||||||
return (
|
.sort((a, b) => b.length - a.length)
|
||||||
Number(b.includes(rootNote.value?.id || '')) -
|
.sort((a, b) => {
|
||||||
Number(a.includes(rootNote.value?.id || ''))
|
return (
|
||||||
)
|
Number(b.includes(rootNote.value?.id || '')) - Number(a.includes(rootNote.value?.id || ''))
|
||||||
})
|
)
|
||||||
.slice(0, 5)
|
})
|
||||||
.map((mindmap): Mindmap => {
|
.slice(0, 5)
|
||||||
const isRoot = mindmap.includes(rootNote.value?.id || '')
|
.map((mindmap): Mindmap => {
|
||||||
return { id: shortid.generate(), notes: mindmap, isRoot }
|
const isRoot = mindmap.includes(rootNote.value?.id || '')
|
||||||
})
|
return { id: shortid.generate(), notes: mindmap, isRoot }
|
||||||
|
})
|
||||||
})
|
})
|
||||||
watch(
|
watch(
|
||||||
mindmaps,
|
mindmaps,
|
||||||
() => {
|
() => {
|
||||||
if (!selectedMindmap.value) selectedMindmap.value = mindmaps.value[0]
|
if (!selectedMindmap.value) selectedMindmap.value = mindmaps.value[0]
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
watch(selectedMindmap, () => setTimeout(() => renderMindmap(), 0), { immediate: true })
|
watch(selectedMindmap, () => setTimeout(() => renderMindmap(), 0), { immediate: true })
|
||||||
|
|
||||||
const nodes = computed(() => {
|
const nodes = computed(() => {
|
||||||
return (
|
return (
|
||||||
Object.entries(notesRelations.value)
|
Object.entries(notesRelations.value)
|
||||||
// .filter(([, relations]) => relations.to.length > 0)
|
// .filter(([, relations]) => relations.to.length > 0)
|
||||||
.filter(([noteId]) => selectedMindmap.value?.notes.includes(noteId))
|
.filter(([noteId]) => selectedMindmap.value?.notes.includes(noteId))
|
||||||
.map(([noteId]) => {
|
.map(([noteId]) => {
|
||||||
return {
|
return {
|
||||||
data: {
|
data: {
|
||||||
id: noteId,
|
id: noteId,
|
||||||
title: getNoteById(noteId)?.title
|
title: getNoteById(noteId)?.title
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
const links = computed(() => {
|
const links = computed(() => {
|
||||||
return Object.entries(notesRelations.value)
|
return Object.entries(notesRelations.value)
|
||||||
.filter(([noteId]) => selectedMindmap.value?.notes.includes(noteId))
|
.filter(([noteId]) => selectedMindmap.value?.notes.includes(noteId))
|
||||||
.filter(([, relations]) => relations.to.length > 0)
|
.filter(([, relations]) => relations.to.length > 0)
|
||||||
.map(([noteId, relations]) => {
|
.map(([noteId, relations]) => {
|
||||||
return relations.to.map((to) => ({
|
return relations.to.map((to) => ({
|
||||||
source: noteId,
|
source: noteId,
|
||||||
target: to
|
target: to
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
.reduce((arr, elem) => arr.concat(elem), [])
|
.reduce((arr, elem) => arr.concat(elem), [])
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex h-full flex-grow flex-col">
|
<div class="flex h-full flex-grow flex-col">
|
||||||
<div class="flex">
|
<UITabs>
|
||||||
<UITabs>
|
<UITab
|
||||||
<UITab
|
v-for="mindmap in mindmaps"
|
||||||
v-for="mindmap in mindmaps"
|
:key="mindmap.id"
|
||||||
:key="mindmap.id"
|
:active="mindmap.id === selectedMindmap?.id"
|
||||||
:active="mindmap.id === selectedMindmap?.id"
|
@click="selectedMindmap = mindmap"
|
||||||
@click="selectedMindmap = mindmap"
|
>
|
||||||
>
|
<i class="fas fa-fw fa-home root mr-1" v-if="mindmap.isRoot" />
|
||||||
<i class="fas fa-fw fa-home root mr-1" v-if="mindmap.isRoot" />
|
{{ mindmap.notes.length }} notes
|
||||||
{{ mindmap.notes.length }} notes
|
</UITab>
|
||||||
</UITab>
|
</UITabs>
|
||||||
</UITabs>
|
<div id="mindmap" ref="mindmapElement" class="h-full"></div>
|
||||||
</div>
|
</div>
|
||||||
<div id="mindmap" ref="mindmapElement" class="h-full"></div>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.mouseover {
|
.mouseover {
|
||||||
@apply hover:cursor-pointer;
|
@apply hover:cursor-pointer;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,74 +1,69 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { formatDate } from '@/utils/helpers'
|
import { formatDate } from '@/utils/helpers'
|
||||||
import {
|
import {
|
||||||
activeNote,
|
activeNote,
|
||||||
deleteNote,
|
deleteNote,
|
||||||
rootNote,
|
rootNote,
|
||||||
setRootNote,
|
setRootNote,
|
||||||
setActiveNote,
|
setActiveNote,
|
||||||
getNoteReferences
|
getNoteReferences
|
||||||
} from '@/composables/useNotes'
|
} from '@/composables/useNotes'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
note: Note
|
note: Note
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
update: [note: Note]
|
update: [note: Note]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const noteTitle = ref<string>(props.note.title)
|
const noteTitle = ref<string>(props.note.title)
|
||||||
watch(noteTitle, () => {
|
watch(noteTitle, () => {
|
||||||
const updatedNote: Note = { ...props.note, title: noteTitle.value }
|
const updatedNote: Note = { ...props.note, title: noteTitle.value }
|
||||||
emit('update', updatedNote)
|
emit('update', updatedNote)
|
||||||
})
|
})
|
||||||
|
|
||||||
const updateNoteContent = (content: string) => {
|
const updateNoteContent = (content: string) => {
|
||||||
const updatedNote: Note = { ...props.note, content }
|
const updatedNote: Note = { ...props.note, content }
|
||||||
emit('update', updatedNote)
|
emit('update', updatedNote)
|
||||||
}
|
}
|
||||||
|
|
||||||
const references = computed<Note[]>(() => getNoteReferences(props.note))
|
const references = computed<Note[]>(() => getNoteReferences(props.note))
|
||||||
|
|
||||||
const handleAction = async (action: 'delete' | 'setRoot', closeModal?: () => Promise<void>) => {
|
const handleAction = async (action: string, closeModal: () => Promise<Boolean>) => {
|
||||||
switch (action) {
|
if (action === 'delete') {
|
||||||
case 'delete':
|
if (closeModal) await closeModal()
|
||||||
if (closeModal) await closeModal()
|
setActiveNote(rootNote.value?.id)
|
||||||
setActiveNote(rootNote.value?.id)
|
deleteNote(props.note.id)
|
||||||
deleteNote(props.note.id)
|
}
|
||||||
break
|
if (action === 'setRoot') {
|
||||||
case 'setRoot':
|
setRootNote(props.note.id)
|
||||||
setRootNote(props.note.id)
|
if (closeModal) closeModal()
|
||||||
if (closeModal) closeModal()
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="flex flex-grow flex-col">
|
<div class="flex flex-grow flex-col">
|
||||||
<NoteToolbar :note="props.note" @execute="handleAction">
|
<NoteToolbar :note="props.note" @execute="handleAction">
|
||||||
<template #title>
|
<template #title>
|
||||||
<i
|
<i
|
||||||
class="fas fa-fw fa-home mr-2 text-base text-secondary opacity-40"
|
class="fas fa-fw fa-home mr-2 text-base text-secondary opacity-40"
|
||||||
v-if="props.note.isRoot"
|
v-if="props.note.isRoot"
|
||||||
></i>
|
></i>
|
||||||
<input
|
<input type="text" class="w-full bg-transparent pb-1 outline-none" v-model="noteTitle" />
|
||||||
type="text"
|
</template>
|
||||||
class="w-full bg-transparent pb-1 outline-none"
|
</NoteToolbar>
|
||||||
v-model="noteTitle"
|
<NoteEditor
|
||||||
/>
|
class="flex-grow"
|
||||||
</template>
|
:note="activeNote"
|
||||||
</NoteToolbar>
|
@update="updateNoteContent"
|
||||||
<NoteEditor
|
v-if="activeNote"
|
||||||
class="flex-grow"
|
></NoteEditor>
|
||||||
:note="activeNote"
|
<NoteReferences :references="references" />
|
||||||
@update="updateNoteContent"
|
<hr class="my-3" />
|
||||||
v-if="activeNote"
|
<div class="flex text-sm text-secondary">
|
||||||
></NoteEditor>
|
<span>{{ note.wordCount }} {{ note.wordCount === 1 ? 'word' : 'words' }}</span>
|
||||||
<NoteReferences :references="references" />
|
<span class="ml-auto">Last modified {{ formatDate(note.modified) }}</span>
|
||||||
<hr class="my-3" />
|
|
||||||
<div class="flex text-sm text-secondary">
|
|
||||||
<span>{{ note.wordCount }} {{ note.wordCount === 1 ? 'word' : 'words' }}</span>
|
|
||||||
<span class="ml-auto">Last modified {{ formatDate(note.modified) }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,31 +1,31 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
color?: 'info' | 'success' | 'warning' | 'error'
|
color?: 'info' | 'success' | 'warning' | 'error'
|
||||||
density?: 'regular' | 'compact'
|
density?: 'regular' | 'compact'
|
||||||
}
|
}
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
color: 'info',
|
color: 'info',
|
||||||
density: 'regular'
|
density: 'regular'
|
||||||
})
|
})
|
||||||
|
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
const colorVariants = {
|
const colorVariants = {
|
||||||
info: 'bg-info',
|
info: 'bg-info',
|
||||||
success: 'bg-success',
|
success: 'bg-success',
|
||||||
warning: 'bg-warning',
|
warning: 'bg-warning',
|
||||||
error: 'bg-error'
|
error: 'bg-error'
|
||||||
}
|
}
|
||||||
const densityVariants = {
|
const densityVariants = {
|
||||||
regular: 'py-4 px-4',
|
regular: 'py-4 px-4',
|
||||||
compact: 'py-2 px-4'
|
compact: 'py-2 px-4'
|
||||||
}
|
}
|
||||||
const colorClass = colorVariants[props.color]
|
const colorClass = colorVariants[props.color]
|
||||||
const densityClass = densityVariants[props.density]
|
const densityClass = densityVariants[props.density]
|
||||||
return [colorClass, densityClass]
|
return [colorClass, densityClass]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="rounded-xl shadow-lg" :class="styleClass">
|
<div class="rounded-xl shadow-lg" :class="styleClass">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,41 +1,41 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
size?: 'xs' | 'sm' | 'md' | 'lg'
|
size?: 'xs' | 'sm' | 'md' | 'lg'
|
||||||
variant?: 'regular' | 'outline' | 'ghost'
|
variant?: 'regular' | 'outline' | 'ghost'
|
||||||
color?: 'regular' | 'info' | 'success' | 'warning' | 'error'
|
color?: 'regular' | 'info' | 'success' | 'warning' | 'error'
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
size: 'md',
|
size: 'md',
|
||||||
variant: 'regular',
|
variant: 'regular',
|
||||||
color: 'regular'
|
color: 'regular'
|
||||||
})
|
})
|
||||||
|
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
const sizeVariants = {
|
const sizeVariants = {
|
||||||
xs: 'dui-badge-xs',
|
xs: 'dui-badge-xs',
|
||||||
sm: 'dui-badge-sm',
|
sm: 'dui-badge-sm',
|
||||||
md: 'dui-badge-md',
|
md: 'dui-badge-md',
|
||||||
lg: 'dui-badge-lg'
|
lg: 'dui-badge-lg'
|
||||||
}
|
}
|
||||||
const variantVariants = {
|
const variantVariants = {
|
||||||
regular: '',
|
regular: '',
|
||||||
outline: 'dui-badge-outline',
|
outline: 'dui-badge-outline',
|
||||||
ghost: 'dui-badge-ghost'
|
ghost: 'dui-badge-ghost'
|
||||||
}
|
}
|
||||||
const colorVariants = {
|
const colorVariants = {
|
||||||
regular: '',
|
regular: '',
|
||||||
info: 'dui-badge-info',
|
info: 'dui-badge-info',
|
||||||
success: 'dui-badge-success',
|
success: 'dui-badge-success',
|
||||||
warning: 'dui-badge-warning',
|
warning: 'dui-badge-warning',
|
||||||
error: 'dui-badge-error'
|
error: 'dui-badge-error'
|
||||||
}
|
}
|
||||||
const sizeClass = sizeVariants[props.size]
|
const sizeClass = sizeVariants[props.size]
|
||||||
const variantClass = variantVariants[props.variant]
|
const variantClass = variantVariants[props.variant]
|
||||||
const colorClass = colorVariants[props.color]
|
const colorClass = colorVariants[props.color]
|
||||||
return [sizeClass, variantClass, colorClass]
|
return [sizeClass, variantClass, colorClass]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<span class="dui-badge" :class="styleClass"><slot></slot></span>
|
<span class="dui-badge" :class="styleClass"><slot></slot></span>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,62 +1,57 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
size?: 'xs' | 'sm' | 'md' | 'lg'
|
size?: 'xs' | 'sm' | 'md' | 'lg'
|
||||||
variant?: 'regular' | 'outline'
|
variant?: 'regular' | 'outline'
|
||||||
color?: 'regular' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'
|
color?: 'regular' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'
|
||||||
dropdown?: boolean
|
dropdown?: boolean
|
||||||
join?: boolean
|
join?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
size: 'md',
|
size: 'md',
|
||||||
variant: 'regular',
|
variant: 'regular',
|
||||||
color: 'regular',
|
color: 'regular',
|
||||||
dropdown: false,
|
dropdown: false,
|
||||||
join: false
|
join: false
|
||||||
})
|
})
|
||||||
|
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
const sizeVariants = {
|
const sizeVariants = {
|
||||||
xs: 'dui-btn-xs',
|
xs: 'dui-btn-xs',
|
||||||
sm: 'dui-btn-sm',
|
sm: 'dui-btn-sm',
|
||||||
md: 'dui-btn-md',
|
md: 'dui-btn-md',
|
||||||
lg: 'dui-btn-lg'
|
lg: 'dui-btn-lg'
|
||||||
}
|
}
|
||||||
const colorVariants = {
|
const colorVariants = {
|
||||||
regular: '',
|
regular: '',
|
||||||
primary: 'dui-btn-primary',
|
primary: 'dui-btn-primary',
|
||||||
secondary: 'dui-btn-secondary',
|
secondary: 'dui-btn-secondary',
|
||||||
info: 'dui-btn-info',
|
info: 'dui-btn-info',
|
||||||
success: 'dui-btn-success',
|
success: 'dui-btn-success',
|
||||||
warning: 'dui-btn-warning',
|
warning: 'dui-btn-warning',
|
||||||
error: 'dui-btn-error'
|
error: 'dui-btn-error'
|
||||||
}
|
}
|
||||||
const variantVariants = {
|
const variantVariants = {
|
||||||
regular: '',
|
regular: '',
|
||||||
outline: 'dui-btn-outline'
|
outline: 'dui-btn-outline'
|
||||||
}
|
}
|
||||||
const sizeClass = sizeVariants[props.size]
|
const sizeClass = sizeVariants[props.size]
|
||||||
const variantClass = variantVariants[props.variant]
|
const variantClass = variantVariants[props.variant]
|
||||||
const colorClass = colorVariants[props.color]
|
const colorClass = colorVariants[props.color]
|
||||||
const joinClass = props.join ? 'dui-join-item' : ''
|
const joinClass = props.join ? 'dui-join-item' : ''
|
||||||
return [sizeClass, variantClass, colorClass, joinClass]
|
return [sizeClass, variantClass, colorClass, joinClass]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<label
|
<label
|
||||||
class="dui-btn h-auto px-3 py-2 duration-0"
|
class="dui-btn h-auto px-3 py-2 duration-0"
|
||||||
:class="styleClass"
|
:class="styleClass"
|
||||||
v-if="props.dropdown"
|
v-if="props.dropdown"
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</label>
|
</label>
|
||||||
<button
|
<button type="button" class="dui-btn h-auto px-3 py-2 duration-0" :class="styleClass" v-else>
|
||||||
type="button"
|
<slot></slot>
|
||||||
class="dui-btn inline-block h-auto max-w-full truncate px-3 py-2 duration-0"
|
</button>
|
||||||
:class="styleClass"
|
|
||||||
v-else
|
|
||||||
>
|
|
||||||
<slot></slot>
|
|
||||||
</button>
|
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dui-join"><slot></slot></div>
|
<div class="dui-join"><slot></slot></div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dui-card bg-base-100 shadow-sm">
|
<div class="dui-card bg-base-100 shadow-sm">
|
||||||
<div class="dui-card-body">
|
<div class="dui-card-body">
|
||||||
<h3 class="dui-card-title" v-if="$slots.title"><slot name="title" /></h3>
|
<h3 class="dui-card-title" v-if="$slots.title"><slot name="title" /></h3>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<div class="dui-card-actions justify-end" v-if="$slots.actions"></div>
|
<div class="dui-card-actions justify-end" v-if="$slots.actions"></div>
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dui-dropdown-end dui-dropdown">
|
<div class="dui-dropdown-end dui-dropdown">
|
||||||
<slot name="activator" tabindex="0"></slot>
|
<slot name="activator" tabindex="0"></slot>
|
||||||
<ul
|
<ul
|
||||||
tabindex="0"
|
tabindex="0"
|
||||||
class="dui-menu-compact dui-dropdown-content dui-menu mt-1 w-52 rounded-box bg-base-100 p-2 text-base-content shadow"
|
class="dui-menu-compact dui-dropdown-content dui-menu rounded-box mt-1 w-52 bg-base-100 p-2 text-base-content shadow"
|
||||||
>
|
>
|
||||||
<slot name="items"></slot>
|
<slot name="items"></slot>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<li class="text-sm">
|
<li class="text-sm">
|
||||||
<a class="rounded-lg"><slot></slot></a>
|
<a class="rounded-lg"><slot></slot></a>
|
||||||
</li>
|
</li>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,35 +1,35 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
modelValue?: any
|
modelValue?: any
|
||||||
color?: 'regular' | 'primary'
|
color?: 'regular' | 'primary'
|
||||||
checked?: boolean
|
checked?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
color: 'regular'
|
color: 'regular'
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'update:modelValue': [value: any]
|
'update:modelValue': [value: any]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
const colorVariants = {
|
const colorVariants = {
|
||||||
regular: '',
|
regular: '',
|
||||||
primary: 'dui-checkbox-primary'
|
primary: 'dui-checkbox-primary'
|
||||||
}
|
}
|
||||||
const colorClass = colorVariants[props.color]
|
const colorClass = colorVariants[props.color]
|
||||||
return [colorClass]
|
return [colorClass]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
class="dui-checkbox dui-checkbox-sm border-secondary"
|
class="dui-checkbox dui-checkbox-sm border-secondary"
|
||||||
:class="styleClass"
|
:class="styleClass"
|
||||||
:checked="props.modelValue || props.checked"
|
:checked="props.modelValue"
|
||||||
@change="emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
|
@change="emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
|
||||||
:disabled="props.disabled"
|
:disabled="props.disabled"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,46 +1,46 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
modelValue?: any
|
modelValue?: any
|
||||||
size?: 'xs' | 'sm' | 'md' | 'lg'
|
size?: 'xs' | 'sm' | 'md' | 'lg'
|
||||||
color?: 'regular' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'
|
color?: 'regular' | 'primary' | 'secondary' | 'info' | 'success' | 'warning' | 'error'
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
size: 'md',
|
size: 'md',
|
||||||
color: 'regular'
|
color: 'regular'
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{
|
const emit = defineEmits<{
|
||||||
'update:modelValue': [value: any]
|
'update:modelValue': [value: any]
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
const sizeVariants = {
|
const sizeVariants = {
|
||||||
xs: 'dui-input-xs',
|
xs: 'dui-input-xs',
|
||||||
sm: 'dui-input-sm',
|
sm: 'dui-input-sm',
|
||||||
md: 'dui-input-md',
|
md: 'dui-input-md',
|
||||||
lg: 'dui-input-lg'
|
lg: 'dui-input-lg'
|
||||||
}
|
}
|
||||||
const colorVariants = {
|
const colorVariants = {
|
||||||
regular: '',
|
regular: '',
|
||||||
primary: 'dui-input-primary',
|
primary: 'dui-input-primary',
|
||||||
secondary: 'dui-input-secondary',
|
secondary: 'dui-input-secondary',
|
||||||
info: 'dui-input-info',
|
info: 'dui-input-info',
|
||||||
success: 'dui-input-success',
|
success: 'dui-input-success',
|
||||||
warning: 'dui-input-warning',
|
warning: 'dui-input-warning',
|
||||||
error: 'dui-input-error'
|
error: 'dui-input-error'
|
||||||
}
|
}
|
||||||
const sizeClass = sizeVariants[props.size]
|
const sizeClass = sizeVariants[props.size]
|
||||||
const colorClass = colorVariants[props.color]
|
const colorClass = colorVariants[props.color]
|
||||||
return [sizeClass, colorClass]
|
return [sizeClass, colorClass]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
:value="props.modelValue"
|
:value="props.modelValue"
|
||||||
@input="emit('update:modelValue', ($event.target as HTMLInputElement)?.value)"
|
@input="emit('update:modelValue', ($event.target as HTMLInputElement)?.value)"
|
||||||
class="dui-input-bordered dui-input my-1 ml-auto mr-1 max-w-xs flex-grow"
|
class="dui-input-bordered dui-input my-1 ml-auto mr-1 max-w-xs flex-grow"
|
||||||
:class="styleClass"
|
:class="styleClass"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
compact?: boolean
|
compact?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
const compactClass = props.compact && 'dui-menu-compact'
|
const compactClass = props.compact && 'dui-menu-compact'
|
||||||
return [compactClass]
|
return [compactClass]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<ul tabindex="0" class="dui-menu rounded-md bg-base-100" :class="styleClass">
|
<ul tabindex="0" class="dui-menu rounded-md bg-base-100" :class="styleClass">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</ul>
|
</ul>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,35 +1,28 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
title?: boolean
|
title?: boolean
|
||||||
disabled?: boolean
|
disabled?: boolean
|
||||||
active?: boolean
|
active?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
const titleClass = props.title
|
const titleClass = props.title
|
||||||
? 'dui-menu-item dui-menu-title !opacity-100 space-x-2 !text-xl font-bold !text-secondary'
|
? 'dui-menu-item dui-menu-title !opacity-100 space-x-2 !text-xl font-bold !text-secondary'
|
||||||
: ''
|
: ''
|
||||||
return [titleClass]
|
return [titleClass]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<li :class="styleClass">
|
<li :class="styleClass">
|
||||||
<span class="flex items-center" v-if="props.title">
|
<span class="flex items-center" v-if="props.title"><slot></slot></span>
|
||||||
<slot></slot>
|
<a
|
||||||
</span>
|
class="flex w-full rounded-md"
|
||||||
<a
|
:class="{ 'dui-disabled': props.disabled, 'dui-active': props.active }"
|
||||||
class="flex w-full rounded-md"
|
v-else
|
||||||
:class="{ 'dui-disabled': props.disabled, 'dui-active': props.active }"
|
>
|
||||||
v-else
|
<slot></slot>
|
||||||
>
|
</a>
|
||||||
<slot></slot>
|
</li>
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
|
||||||
.dui-active {
|
|
||||||
@apply bg-primary;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|||||||
@@ -1,41 +1,42 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onClickOutside } from '@vueuse/core'
|
import { onClickOutside } from '@vueuse/core'
|
||||||
import { vibrate } from '@/composables/useHaptics'
|
import { Haptics, ImpactStyle } from '@capacitor/haptics'
|
||||||
|
|
||||||
const props = withDefaults(
|
const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
open?: boolean
|
open?: boolean
|
||||||
persistent?: boolean
|
persistent?: boolean
|
||||||
size?: 'sm' | 'md' | 'lg'
|
size?: 'sm' | 'md' | 'lg'
|
||||||
icon?: string
|
icon?: string
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
open: false,
|
open: false,
|
||||||
persistent: false,
|
persistent: false,
|
||||||
size: 'md'
|
size: 'md'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
const show = ref<boolean>(false)
|
const show = ref<boolean>(false)
|
||||||
watch(
|
watch(
|
||||||
() => props.open,
|
() => props.open,
|
||||||
() => {
|
() => {
|
||||||
if (show.value) vibrate()
|
if (show.value) {
|
||||||
|
Haptics.impact({ style: ImpactStyle.Light })
|
||||||
show.value = props.open
|
}
|
||||||
},
|
show.value = props.open
|
||||||
{ immediate: true }
|
},
|
||||||
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
const modal = ref<HTMLElement | null>(null)
|
const modal = ref<HTMLElement | null>(null)
|
||||||
const modalBox = ref(null)
|
const modalBox = ref(null)
|
||||||
|
|
||||||
const open = () => (show.value = true)
|
const open = () => (show.value = true)
|
||||||
const close = (): Promise<void> => {
|
const close = (): Promise<boolean> => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
modal.value?.addEventListener('transitionend', () => resolve())
|
modal.value?.addEventListener('transitionend', () => resolve(true))
|
||||||
show.value = false
|
show.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const slotProps = { open, close }
|
const slotProps = { open, close }
|
||||||
@@ -43,48 +44,48 @@ const slotProps = { open, close }
|
|||||||
if (!props.persistent) onClickOutside(modalBox, () => close())
|
if (!props.persistent) onClickOutside(modalBox, () => close())
|
||||||
|
|
||||||
const onEnter = (el: Element, done: () => void): void => {
|
const onEnter = (el: Element, done: () => void): void => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
el.classList.add('dui-modal-open')
|
el.classList.add('dui-modal-open')
|
||||||
done()
|
done()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
const onLeave = (el: Element, done: () => void): void => {
|
const onLeave = (el: Element, done: () => void): void => {
|
||||||
el.classList.remove('dui-modal-open')
|
el.classList.remove('dui-modal-open')
|
||||||
el.addEventListener('transitionend', () => done())
|
el.addEventListener('transitionend', () => done())
|
||||||
}
|
}
|
||||||
|
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
const sizeVariants = {
|
const sizeVariants = {
|
||||||
sm: 'max-w-xs',
|
sm: 'max-w-xs',
|
||||||
md: 'max-w-md',
|
md: 'max-w-md',
|
||||||
lg: 'max-w-2xl'
|
lg: 'max-w-2xl'
|
||||||
}
|
}
|
||||||
const sizeClass = sizeVariants[props.size]
|
const sizeClass = sizeVariants[props.size]
|
||||||
return [sizeClass]
|
return [sizeClass]
|
||||||
})
|
})
|
||||||
|
|
||||||
defineExpose({ open, close })
|
defineExpose({ open, close })
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<slot name="activator" v-bind="slotProps"></slot>
|
<slot name="activator" v-bind="slotProps"></slot>
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<Transition @enter="onEnter" @leave="onLeave" appear>
|
<Transition @enter="onEnter" @leave="onLeave" appear>
|
||||||
<div class="dui-modal bg-neutral-800 bg-opacity-60" v-if="show" ref="modal">
|
<div class="dui-modal bg-neutral-800 bg-opacity-60" v-if="show" ref="modal">
|
||||||
<div class="dui-modal-box" :class="styleClass" ref="modalBox">
|
<div class="dui-modal-box" :class="styleClass" ref="modalBox">
|
||||||
<h3 class="mb-4 flex items-center text-xl font-bold" v-if="$slots.title">
|
<h3 class="mb-4 flex items-center text-xl font-bold" v-if="$slots.title">
|
||||||
<slot name="title" />
|
<slot name="title" />
|
||||||
</h3>
|
</h3>
|
||||||
<div>
|
<div>
|
||||||
<slot v-bind="slotProps" />
|
<slot v-bind="slotProps" />
|
||||||
</div>
|
</div>
|
||||||
<div class="dui-modal-action mt-4">
|
<div class="dui-modal-action mt-4">
|
||||||
<slot name="actions" v-bind="slotProps">
|
<slot name="actions" v-bind="slotProps">
|
||||||
<UIButton size="sm" @click="close">Close</UIButton>
|
<UIButton size="sm" @click="close">Close</UIButton>
|
||||||
</slot>
|
</slot>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
active?: boolean
|
active?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
const activeClass = props.active && 'dui-tab-active font-bold !border-primary text-primary'
|
const activeClass = props.active && 'dui-tab-active !border-primary text-primary'
|
||||||
return [activeClass]
|
return [activeClass]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<a class="dui-tab-bordered dui-tab hover:font-bold hover:text-primary" :class="styleClass">
|
<a class="dui-tab-bordered dui-tab-lifted dui-tab dui-tab-md" :class="styleClass">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</a>
|
</a>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
interface Props {
|
interface Props {
|
||||||
size?: 'xs' | 'sm' | 'md' | 'lg'
|
size?: 'xs' | 'sm' | 'md' | 'lg'
|
||||||
}
|
}
|
||||||
const props = withDefaults(defineProps<Props>(), {
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
size: 'md'
|
size: 'md'
|
||||||
})
|
})
|
||||||
|
|
||||||
const styleClass = computed(() => {
|
const styleClass = computed(() => {
|
||||||
const sizeVariants = {
|
const sizeVariants = {
|
||||||
xs: 'dui-table-xs',
|
xs: 'dui-table-xs',
|
||||||
sm: 'dui-table-sm',
|
sm: 'dui-table-sm',
|
||||||
md: 'dui-table-md',
|
md: 'dui-table-md',
|
||||||
lg: 'dui-table-lg'
|
lg: 'dui-table-lg'
|
||||||
}
|
}
|
||||||
const sizeClass = sizeVariants[props.size]
|
const sizeClass = sizeVariants[props.size]
|
||||||
return [sizeClass]
|
return [sizeClass]
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<table class="dui-table" :class="styleClass"><slot></slot></table>
|
<table class="dui-table" :class="styleClass"><slot></slot></table>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dui-tabs dui-tabs-boxed dui-tabs-md"><slot></slot></div>
|
<div class="dui-tabs"><slot></slot></div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,156 +5,150 @@ import { preferredNotesSource } from '@/composables/useSettings'
|
|||||||
import { activeNotesSource, syncNotesToFirebase, baseNotes } from '@/composables/useNotes'
|
import { activeNotesSource, syncNotesToFirebase, baseNotes } from '@/composables/useNotes'
|
||||||
|
|
||||||
function getClientKeysFromLocalStorage(): { [uid: string]: string } {
|
function getClientKeysFromLocalStorage(): { [uid: string]: string } {
|
||||||
try {
|
try {
|
||||||
return JSON.parse(localStorage.getItem('clientKeys') || '{}')
|
return JSON.parse(localStorage.getItem('clientKeys') || '{}')
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return {}
|
return {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const clientKey = ref<ClientKey>()
|
export const clientKey = ref<ClientKey>()
|
||||||
|
|
||||||
export const getClientKey = () => {
|
export const getClientKey = () => {
|
||||||
if (!user.value) return
|
if (!user.value) return
|
||||||
const clientKeys = getClientKeysFromLocalStorage()
|
const clientKeys = getClientKeysFromLocalStorage()
|
||||||
clientKey.value = clientKeys[user.value?.uid]
|
clientKey.value = clientKeys[user.value?.uid]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const setClientKey = (passphrase: string) => {
|
export const setClientKey = (passphrase: string) => {
|
||||||
const calculatedClientKey = calculateClientKey(passphrase)
|
const calculatedClientKey = calculateClientKey(passphrase)
|
||||||
const verified = verifyClientKey(calculatedClientKey)
|
const verified = verifyClientKey(calculatedClientKey)
|
||||||
if (!user.value || !verified) return false
|
if (!user.value || !verified) return false
|
||||||
const clientKeys = getClientKeysFromLocalStorage()
|
const clientKeys = getClientKeysFromLocalStorage()
|
||||||
clientKeys[user.value.uid] = calculatedClientKey
|
clientKeys[user.value.uid] = calculatedClientKey
|
||||||
localStorage.setItem('clientKeys', JSON.stringify(clientKeys))
|
localStorage.setItem('clientKeys', JSON.stringify(clientKeys))
|
||||||
clientKey.value = calculatedClientKey
|
clientKey.value = calculatedClientKey
|
||||||
getEncryptionKey()
|
getEncryptionKey()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
export const verifyClientKey = (clientKey: ClientKey) => {
|
export const verifyClientKey = (clientKey: ClientKey) => {
|
||||||
try {
|
try {
|
||||||
if (!encryptedEncryptionKey.value) throw new Error('Encryption key is null')
|
if (!encryptedEncryptionKey.value) throw new Error('Encryption key is null')
|
||||||
if (!clientKey) throw new Error('Client key is null')
|
if (!clientKey) throw new Error('Client key is null')
|
||||||
decrypt(encryptedEncryptionKey.value, clientKey)
|
decrypt(encryptedEncryptionKey.value, clientKey)
|
||||||
return true
|
return true
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const removeClientKey = () => {
|
const removeClientKey = () => {
|
||||||
if (!user.value) return
|
if (!user.value) return
|
||||||
const clientKeys = structuredClone(getClientKeysFromLocalStorage())
|
const clientKeys = structuredClone(getClientKeysFromLocalStorage())
|
||||||
delete clientKeys[user.value?.uid]
|
delete clientKeys[user.value?.uid]
|
||||||
localStorage.setItem('clientKeys', JSON.stringify(clientKeys))
|
localStorage.setItem('clientKeys', JSON.stringify(clientKeys))
|
||||||
}
|
}
|
||||||
|
|
||||||
const encryptedEncryptionKey = ref<EncryptedEncryptionKey | null>()
|
const encryptedEncryptionKey = ref<EncryptedEncryptionKey | null>()
|
||||||
|
|
||||||
async function getEncryptedEncryptionKey(): Promise<EncryptedEncryptionKey | void> {
|
async function getEncryptedEncryptionKey(): Promise<EncryptedEncryptionKey | void> {
|
||||||
if (!user.value || !db.value) return
|
if (!user.value || !db.value) return
|
||||||
const data = (await getDoc(doc(db.value, 'encryptionKeys', user.value.uid))).data()
|
const data = (await getDoc(doc(db.value, 'encryptionKeys', user.value.uid))).data()
|
||||||
return data?.key
|
return data?.key
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setEncryptedEncryptionKey(encryptedEncryptionKey: EncryptedEncryptionKey | null) {
|
async function setEncryptedEncryptionKey(encryptedEncryptionKey: EncryptedEncryptionKey | null) {
|
||||||
if (!user.value || !db.value) return
|
if (!user.value || !db.value) return
|
||||||
const docRef = doc(db.value, 'encryptionKeys', user.value.uid)
|
const docRef = doc(db.value, 'encryptionKeys', user.value.uid)
|
||||||
await setDoc(docRef, { key: encryptedEncryptionKey })
|
await setDoc(docRef, { key: encryptedEncryptionKey })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const encryptionKey = ref<EncryptionKey | null>()
|
export const encryptionKey = ref<EncryptionKey | null>()
|
||||||
|
|
||||||
export async function getEncryptionKey() {
|
export async function getEncryptionKey() {
|
||||||
encryptedEncryptionKey.value = (await getEncryptedEncryptionKey()) || undefined
|
encryptedEncryptionKey.value = (await getEncryptedEncryptionKey()) || undefined
|
||||||
if (encryptedEncryptionKey.value && clientKey.value) {
|
if (encryptedEncryptionKey.value && clientKey.value) {
|
||||||
encryptionKey.value = decrypt(encryptedEncryptionKey.value, clientKey.value)
|
encryptionKey.value = decrypt(encryptedEncryptionKey.value, clientKey.value)
|
||||||
} else if (!encryptedEncryptionKey.value) {
|
} else if (!encryptedEncryptionKey.value) {
|
||||||
encryptionKey.value = null
|
encryptionKey.value = null
|
||||||
} else {
|
} else {
|
||||||
encryptionKey.value = undefined
|
encryptionKey.value = undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const passphraseRequired = computed(() => {
|
export const passphraseRequired = computed(() => {
|
||||||
return Boolean(encryptedEncryptionKey.value && !clientKey.value)
|
return Boolean(encryptedEncryptionKey.value && !clientKey.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const decryptNote = (note: BaseNote, key: EncryptionKey) => {
|
const decryptNote = (note: BaseNote, key: EncryptionKey) => {
|
||||||
try {
|
try {
|
||||||
return {
|
return {
|
||||||
...note,
|
...note,
|
||||||
title: decrypt(note.title, key),
|
title: decrypt(note.title, key),
|
||||||
content: decrypt(note.content, key)
|
content: decrypt(note.content, key)
|
||||||
}
|
|
||||||
} catch (error: any) {
|
|
||||||
console.error(error)
|
|
||||||
return note
|
|
||||||
}
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error(error)
|
||||||
|
return note
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decryptNotes = (notes: BaseNotes, encryptionKey: EncryptionKey) => {
|
export const decryptNotes = (notes: BaseNotes, encryptionKey: EncryptionKey) => {
|
||||||
const decryptedNotes = Object.fromEntries(
|
const decryptedNotes = Object.fromEntries(
|
||||||
Object.entries(notes).map(([noteId, note]) => [
|
Object.entries(notes).map(([noteId, note]) => [noteId, { ...decryptNote(note, encryptionKey) }])
|
||||||
noteId,
|
)
|
||||||
{ ...decryptNote(note, encryptionKey) }
|
return decryptedNotes
|
||||||
])
|
|
||||||
)
|
|
||||||
return decryptedNotes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const encryptNote = (note: BaseNote, key: EncryptionKey) => {
|
const encryptNote = (note: BaseNote, key: EncryptionKey) => {
|
||||||
return {
|
return {
|
||||||
...note,
|
...note,
|
||||||
title: encrypt(note.title, key),
|
title: encrypt(note.title, key),
|
||||||
content: encrypt(note.content, key)
|
content: encrypt(note.content, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const encryptNotes = (notes: BaseNotes, encryptionKey: EncryptionKey) => {
|
export const encryptNotes = (notes: BaseNotes, encryptionKey: EncryptionKey) => {
|
||||||
const encryptedNotes = Object.fromEntries(
|
const encryptedNotes = Object.fromEntries(
|
||||||
Object.entries(notes).map(([noteId, note]) => [
|
Object.entries(notes).map(([noteId, note]) => [noteId, { ...encryptNote(note, encryptionKey) }])
|
||||||
noteId,
|
)
|
||||||
{ ...encryptNote(note, encryptionKey) }
|
return encryptedNotes
|
||||||
])
|
|
||||||
)
|
|
||||||
return encryptedNotes
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const verifyPassphrase = (passphrase: string) => {
|
export const verifyPassphrase = (passphrase: string) => {
|
||||||
const calculatedClientKey = calculateClientKey(passphrase)
|
const calculatedClientKey = calculateClientKey(passphrase)
|
||||||
return calculatedClientKey === clientKey.value
|
return calculatedClientKey === clientKey.value
|
||||||
}
|
}
|
||||||
|
|
||||||
export const disableEncryption = async (passphrase: string) => {
|
export const disableEncryption = async (passphrase: string) => {
|
||||||
if (!encryptionKey.value) return "Encryption key doesn't exist."
|
if (!encryptionKey.value) return "Encryption key doesn't exist."
|
||||||
if (!verifyPassphrase(passphrase)) return 'Passphrase is incorrect.'
|
if (!verifyPassphrase(passphrase)) return 'Passphrase is incorrect.'
|
||||||
preferredNotesSource.value = 'firebase'
|
preferredNotesSource.value = 'firebase'
|
||||||
if (activeNotesSource.value !== 'firebase') throw Error('Something went wrong.')
|
if (activeNotesSource.value !== 'firebase') throw Error('Something went wrong.')
|
||||||
await setEncryptedEncryptionKey(null)
|
await setEncryptedEncryptionKey(null)
|
||||||
encryptedEncryptionKey.value = null
|
encryptedEncryptionKey.value = null
|
||||||
encryptionKey.value = undefined
|
encryptionKey.value = undefined
|
||||||
removeClientKey()
|
removeClientKey()
|
||||||
await syncNotesToFirebase(baseNotes.value)
|
await syncNotesToFirebase(baseNotes.value)
|
||||||
getEncryptionKey()
|
getEncryptionKey()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const enableEncryption = async (passphrase: string) => {
|
export const enableEncryption = async (passphrase: string) => {
|
||||||
preferredNotesSource.value = 'firebase'
|
preferredNotesSource.value = 'firebase'
|
||||||
if (activeNotesSource.value !== 'firebase') throw Error('Something went wrong.')
|
if (activeNotesSource.value !== 'firebase') throw Error('Something went wrong.')
|
||||||
const candidateEncryptionKey = generateEncryptionKey()
|
const candidateEncryptionKey = generateEncryptionKey()
|
||||||
const candidateClientKey = calculateClientKey(passphrase)
|
const candidateClientKey = calculateClientKey(passphrase)
|
||||||
const candidateEncryptedEncryptionKey = encrypt(candidateEncryptionKey, candidateClientKey)
|
const candidateEncryptedEncryptionKey = encrypt(candidateEncryptionKey, candidateClientKey)
|
||||||
await setEncryptedEncryptionKey(candidateEncryptedEncryptionKey)
|
await setEncryptedEncryptionKey(candidateEncryptedEncryptionKey)
|
||||||
encryptedEncryptionKey.value = candidateEncryptedEncryptionKey
|
encryptedEncryptionKey.value = candidateEncryptedEncryptionKey
|
||||||
encryptionKey.value = candidateEncryptionKey
|
encryptionKey.value = candidateEncryptionKey
|
||||||
setClientKey(passphrase)
|
setClientKey(passphrase)
|
||||||
await syncNotesToFirebase(baseNotes.value)
|
await syncNotesToFirebase(baseNotes.value)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const clearEncryptionKeys = () => {
|
export const clearEncryptionKeys = () => {
|
||||||
encryptionKey.value = undefined
|
encryptionKey.value = undefined
|
||||||
encryptedEncryptionKey.value = undefined
|
encryptedEncryptionKey.value = undefined
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,16 +5,16 @@ import { Capacitor } from '@capacitor/core'
|
|||||||
import { initializeApp } from 'firebase/app'
|
import { initializeApp } from 'firebase/app'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
initializeFirestore,
|
initializeFirestore,
|
||||||
persistentLocalCache,
|
persistentLocalCache,
|
||||||
persistentMultipleTabManager
|
persistentMultipleTabManager
|
||||||
} from 'firebase/firestore'
|
} from 'firebase/firestore'
|
||||||
import {
|
import {
|
||||||
getAuth,
|
getAuth,
|
||||||
indexedDBLocalPersistence,
|
indexedDBLocalPersistence,
|
||||||
initializeAuth,
|
initializeAuth,
|
||||||
onAuthStateChanged,
|
onAuthStateChanged,
|
||||||
signOut as firebaseSignOut
|
signOut as firebaseSignOut
|
||||||
} from 'firebase/auth'
|
} from 'firebase/auth'
|
||||||
import { type FirebaseApp } from 'firebase/app'
|
import { type FirebaseApp } from 'firebase/app'
|
||||||
import { type User } from '@firebase/auth'
|
import { type User } from '@firebase/auth'
|
||||||
@@ -27,14 +27,14 @@ import type { Firestore } from 'firebase/firestore'
|
|||||||
// Your web app's Firebase configuration
|
// Your web app's Firebase configuration
|
||||||
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
|
// For Firebase JS SDK v7.20.0 and later, measurementId is optional
|
||||||
const firebaseConfig = {
|
const firebaseConfig = {
|
||||||
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
|
apiKey: import.meta.env.VITE_FIREBASE_API_KEY,
|
||||||
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
|
authDomain: import.meta.env.VITE_FIREBASE_AUTH_DOMAIN,
|
||||||
databaseURL: import.meta.env.VITE_FIREBASE_DATABASE_URL,
|
databaseURL: import.meta.env.VITE_FIREBASE_DATABASE_URL,
|
||||||
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
|
projectId: import.meta.env.VITE_FIREBASE_PROJECT_ID,
|
||||||
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
|
storageBucket: import.meta.env.VITE_FIREBASE_STORAGE_BUCKET,
|
||||||
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
|
messagingSenderId: import.meta.env.VITE_FIREBASE_MESSAGING_SENDER_ID,
|
||||||
appId: import.meta.env.VITE_FIREBASE_APP_ID,
|
appId: import.meta.env.VITE_FIREBASE_APP_ID,
|
||||||
measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID
|
measurementId: import.meta.env.VITE_FIREBASE_MEASUREMENT_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
export const user = ref<User | null>()
|
export const user = ref<User | null>()
|
||||||
@@ -46,26 +46,25 @@ export const signOut = async () => firebaseSignOut(getAuth())
|
|||||||
export const db = ref<Firestore>()
|
export const db = ref<Firestore>()
|
||||||
|
|
||||||
const getFirebaseAuth = async (app: FirebaseApp) => {
|
const getFirebaseAuth = async (app: FirebaseApp) => {
|
||||||
if (Capacitor.isNativePlatform()) {
|
if (Capacitor.isNativePlatform()) {
|
||||||
return initializeAuth(app, {
|
return initializeAuth(app, {
|
||||||
persistence: indexedDBLocalPersistence
|
persistence: indexedDBLocalPersistence
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
return getAuth()
|
return getAuth()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Firebase
|
// Initialize Firebase
|
||||||
export const initializeFirebase = async () => {
|
export const initializeFirebase = async () => {
|
||||||
const app = initializeApp(firebaseConfig)
|
const app = initializeApp(firebaseConfig)
|
||||||
const auth = await getFirebaseAuth(app)
|
const auth = await getFirebaseAuth(app)
|
||||||
onAuthStateChanged(auth, (firebaseUser) => {
|
onAuthStateChanged(auth, (firebaseUser) => {
|
||||||
console.log('auth state changed', firebaseUser)
|
user.value = firebaseUser
|
||||||
user.value = firebaseUser
|
})
|
||||||
|
db.value = markRaw(
|
||||||
|
initializeFirestore(app, {
|
||||||
|
localCache: persistentLocalCache({ tabManager: persistentMultipleTabManager() })
|
||||||
})
|
})
|
||||||
db.value = markRaw(
|
)
|
||||||
initializeFirestore(app, {
|
|
||||||
localCache: persistentLocalCache({ tabManager: persistentMultipleTabManager() })
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
import { Haptics, ImpactStyle } from '@capacitor/haptics'
|
|
||||||
|
|
||||||
export const vibrate = () => Haptics.impact({ style: ImpactStyle.Light })
|
|
||||||
@@ -8,17 +8,17 @@ import { defaultNotes } from '@/utils/defaultNotes'
|
|||||||
import { mdToHtml } from '@/utils/markdown'
|
import { mdToHtml } from '@/utils/markdown'
|
||||||
import { getAllMatches } from '@/utils/helpers'
|
import { getAllMatches } from '@/utils/helpers'
|
||||||
import { preferredNotesSource } from '@/composables/useSettings'
|
import { preferredNotesSource } from '@/composables/useSettings'
|
||||||
import { vibrate } from '@/composables/useHaptics'
|
import { Haptics, ImpactStyle } from '@capacitor/haptics'
|
||||||
|
|
||||||
export const notesSources = computed(() => ({
|
export const notesSources = computed(() => ({
|
||||||
local: true,
|
local: true,
|
||||||
firebase: initialized.value && user.value
|
firebase: initialized.value && user.value
|
||||||
}))
|
}))
|
||||||
|
|
||||||
export const availableNotesSources = computed<notesSourceValues[]>(() =>
|
export const availableNotesSources = computed<notesSourceValues[]>(() =>
|
||||||
Object.entries(notesSources.value)
|
Object.entries(notesSources.value)
|
||||||
.filter(([, enabled]) => enabled)
|
.filter(([, enabled]) => enabled)
|
||||||
.map(([source]) => source as notesSourceValues)
|
.map(([source]) => source as notesSourceValues)
|
||||||
)
|
)
|
||||||
|
|
||||||
export type notesSourceValues = keyof typeof notesSources.value
|
export type notesSourceValues = keyof typeof notesSources.value
|
||||||
@@ -26,255 +26,249 @@ export type notesSourceValues = keyof typeof notesSources.value
|
|||||||
export const activeNotesSource = ref<notesSourceValues | null>(null)
|
export const activeNotesSource = ref<notesSourceValues | null>(null)
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
const getSource = (): notesSourceValues | null => {
|
const getSource = (): notesSourceValues | null => {
|
||||||
if (!initialized.value) return null
|
if (!initialized.value) return null
|
||||||
if (
|
if (
|
||||||
preferredNotesSource.value &&
|
preferredNotesSource.value &&
|
||||||
availableNotesSources.value.includes(preferredNotesSource.value)
|
availableNotesSources.value.includes(preferredNotesSource.value)
|
||||||
) {
|
) {
|
||||||
return preferredNotesSource.value
|
return preferredNotesSource.value
|
||||||
} else {
|
} else {
|
||||||
return user.value ? 'firebase' : 'local'
|
return user.value ? 'firebase' : 'local'
|
||||||
}
|
|
||||||
}
|
}
|
||||||
activeNotesSource.value = getSource()
|
}
|
||||||
|
activeNotesSource.value = getSource()
|
||||||
})
|
})
|
||||||
|
|
||||||
export const baseNotes = ref<BaseNotes>({})
|
export const baseNotes = ref<BaseNotes>({})
|
||||||
|
|
||||||
const syncNotesLocal = (notes: BaseNotes) => {
|
const syncNotesLocal = (notes: BaseNotes) => {
|
||||||
localStorage.setItem('notes', JSON.stringify(notes))
|
localStorage.setItem('notes', JSON.stringify(notes))
|
||||||
}
|
}
|
||||||
|
|
||||||
// export const syncNotesToFirebase = async (newNotes: BaseNotes, oldNotes?: BaseNotes) => {
|
// export const syncNotesToFirebase = async (newNotes: BaseNotes, oldNotes?: BaseNotes) => {
|
||||||
export const syncNotesToFirebase = async (baseNotes: BaseNotes) => {
|
export const syncNotesToFirebase = async (baseNotes: BaseNotes) => {
|
||||||
if (!db.value) throw Error("Database undefined, can't sync to Firebase")
|
if (!db.value) throw Error("Database undefined, can't sync to Firebase")
|
||||||
if (!user.value) throw Error("User undefined, can't sync to Firebase")
|
if (!user.value) throw Error("User undefined, can't sync to Firebase")
|
||||||
const notes = encryptionKey.value ? encryptNotes(baseNotes, encryptionKey.value) : baseNotes
|
const notes = encryptionKey.value ? encryptNotes(baseNotes, encryptionKey.value) : baseNotes
|
||||||
try {
|
try {
|
||||||
const docRef = doc(db.value, 'pages', user.value.uid)
|
const docRef = doc(db.value, 'pages', user.value.uid)
|
||||||
// if (oldNotes) {
|
// if (oldNotes) {
|
||||||
// const notesToDelete = Object.keys(oldNotes).filter((x) => !Object.keys(newNotes).includes(x))
|
// const notesToDelete = Object.keys(oldNotes).filter((x) => !Object.keys(newNotes).includes(x))
|
||||||
// await Promise.all(
|
// await Promise.all(
|
||||||
// notesToDelete.map((noteId: string) => {
|
// notesToDelete.map((noteId: string) => {
|
||||||
// return updateDoc(docRef, { [noteId]: deleteField() })
|
// return updateDoc(docRef, { [noteId]: deleteField() })
|
||||||
// })
|
// })
|
||||||
// )
|
// )
|
||||||
// }
|
// }
|
||||||
await setDoc(docRef, notes)
|
await setDoc(docRef, notes)
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
baseNotes,
|
baseNotes,
|
||||||
async () => {
|
async () => {
|
||||||
if (!activeNotesSource.value || Object.keys(baseNotes.value).length === 0) return
|
if (!activeNotesSource.value || Object.keys(baseNotes.value).length === 0) return
|
||||||
if (activeNotesSource.value === 'local') {
|
if (activeNotesSource.value === 'local') {
|
||||||
syncNotesLocal(baseNotes.value)
|
syncNotesLocal(baseNotes.value)
|
||||||
} else if (activeNotesSource.value === 'firebase') {
|
} else if (activeNotesSource.value === 'firebase') {
|
||||||
syncNotesToFirebase(baseNotes.value)
|
syncNotesToFirebase(baseNotes.value)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ deep: true }
|
{ deep: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
export const notes = computed<Note[]>(() => {
|
export const notes = computed<Note[]>(() => {
|
||||||
return Object.entries(baseNotes.value)
|
return Object.entries(baseNotes.value)
|
||||||
.map(([, note]) => ({
|
.map(([, note]) => ({
|
||||||
...note,
|
...note,
|
||||||
wordCount: note.content.split(' ').filter((word) => word.length > 0).length
|
wordCount: note.content.split(' ').filter((word) => word.length > 0).length
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => b.modified - a.modified) as Note[]
|
.sort((a, b) => b.modified - a.modified) as Note[]
|
||||||
})
|
})
|
||||||
watch(notes, () => {
|
watch(notes, () => {
|
||||||
if (notes.value.length > 0 && !activeNote.value && activeViewMode.value.name === 'Note')
|
if (notes.value.length > 0 && !activeNote.value && activeViewMode.value.name === 'Note')
|
||||||
setActiveNote(rootNote.value?.id, false)
|
setActiveNote(rootNote.value?.id, false)
|
||||||
})
|
})
|
||||||
|
|
||||||
const activeNoteId = ref<string>()
|
const activeNoteId = ref<string>()
|
||||||
export const activeNote = computed(() => notes.value.find((note) => note.id === activeNoteId.value))
|
export const activeNote = computed(() => notes.value.find((note) => note.id === activeNoteId.value))
|
||||||
watch(activeNote, () => {
|
watch(activeNote, () => {
|
||||||
if (activeNote.value) {
|
if (activeNote.value) {
|
||||||
useTitle(`${activeNote.value.title} | Contexted`)
|
useTitle(`${activeNote.value.title} | Contexted`)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export const setActiveNote = (noteId: string | undefined, haptic: boolean = true) => {
|
export const setActiveNote = (noteId: string | undefined, haptic: boolean = true) => {
|
||||||
if (noteId) {
|
if (noteId) {
|
||||||
activeNoteId.value = noteId
|
activeNoteId.value = noteId
|
||||||
activeViewMode.value = viewModes.find((mode) => mode.name === 'Note') || viewModes[0]
|
activeViewMode.value = viewModes.find((mode) => mode.name === 'Note') || viewModes[0]
|
||||||
if (haptic) vibrate()
|
if (haptic) Haptics.impact({ style: ImpactStyle.Light })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const rootNote = computed<Note | undefined>(() => {
|
export const rootNote = computed<Note | undefined>(() => {
|
||||||
const rootNote = notes.value.find((note: Note) => note.isRoot)
|
const rootNote = notes.value.find((note: Note) => note.isRoot)
|
||||||
return rootNote
|
return rootNote
|
||||||
})
|
})
|
||||||
|
|
||||||
export const setRootNote = (noteId: string) => {
|
export const setRootNote = (noteId: string) => {
|
||||||
if (rootNote.value) {
|
if (rootNote.value) {
|
||||||
const updatedRootNote = { ...baseNotes.value[rootNote.value.id], isRoot: false }
|
const updatedRootNote = { ...baseNotes.value[rootNote.value.id], isRoot: false }
|
||||||
updateNote(updatedRootNote.id, updatedRootNote)
|
updateNote(updatedRootNote.id, updatedRootNote)
|
||||||
}
|
}
|
||||||
const note = { ...baseNotes.value[noteId], isRoot: true }
|
const note = { ...baseNotes.value[noteId], isRoot: true }
|
||||||
updateNote(noteId, note)
|
updateNote(noteId, note)
|
||||||
setActiveNote(noteId, false)
|
setActiveNote(noteId, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const insertDefaultNotes = (defaultNotes: BaseNote[]) => {
|
export const insertDefaultNotes = (defaultNotes: BaseNote[]) => {
|
||||||
defaultNotes.forEach((defaultNote) => {
|
defaultNotes.forEach((defaultNote) => {
|
||||||
baseNotes.value[defaultNote.id] = defaultNote
|
baseNotes.value[defaultNote.id] = defaultNote
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getNoteById = (noteId: string) => {
|
export const getNoteById = (noteId: string) => {
|
||||||
return notes.value.find((note) => note.id === noteId)
|
return notes.value.find((note) => note.id === noteId)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getNoteByTitle = (title: string) => {
|
export const getNoteByTitle = (title: string) => {
|
||||||
return notes.value.find((note) => note.title === title)
|
return notes.value.find((note) => note.title === title)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const findNotesByByTitle = (title: string) => {
|
export const findNotesByByTitle = (title: string) => {
|
||||||
const titleLowerCase = title.toLowerCase()
|
const titleLowerCase = title.toLowerCase()
|
||||||
return notes.value.filter((note) => note.title.toLowerCase().includes(titleLowerCase))
|
return notes.value.filter((note) => note.title.toLowerCase().includes(titleLowerCase))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const findNotes = (query: string): Note[] => {
|
export const findNotes = (query: string): Note[] => {
|
||||||
const removeMdFromText = (mdText: string): string => {
|
const removeMdFromText = (mdText: string): string => {
|
||||||
const div = document.createElement('div')
|
const div = document.createElement('div')
|
||||||
div.innerHTML = mdToHtml(mdText)
|
div.innerHTML = mdToHtml(mdText)
|
||||||
const textWithoutMd = div.textContent || div.innerText || ''
|
const textWithoutMd = div.textContent || div.innerText || ''
|
||||||
return textWithoutMd
|
return textWithoutMd
|
||||||
}
|
}
|
||||||
return notes.value.filter((note) => {
|
return notes.value.filter((note) => {
|
||||||
const matchTitle = note.title.toLowerCase().includes(query.toLowerCase())
|
const matchTitle = note.title.toLowerCase().includes(query.toLowerCase())
|
||||||
const matchContent = removeMdFromText(note.content)
|
const matchContent = removeMdFromText(note.content).toLowerCase().includes(query.toLowerCase())
|
||||||
.toLowerCase()
|
return matchTitle || matchContent
|
||||||
.includes(query.toLowerCase())
|
})
|
||||||
return matchTitle || matchContent
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const updateNote = (noteId: string, note: BaseNote) => {
|
export const updateNote = (noteId: string, note: BaseNote) => {
|
||||||
const updatedNote: BaseNote = {
|
const updatedNote: BaseNote = {
|
||||||
...note,
|
...note,
|
||||||
modified: new Date().getTime()
|
modified: new Date().getTime()
|
||||||
}
|
}
|
||||||
baseNotes.value[noteId] = updatedNote
|
baseNotes.value[noteId] = updatedNote
|
||||||
}
|
}
|
||||||
|
|
||||||
export const addNote = (title: string, content: string, goToNote: boolean = false) => {
|
export const addNote = (title: string, content: string, goToNote: boolean = false) => {
|
||||||
const id = shortid.generate()
|
const id = shortid.generate()
|
||||||
const newNote: BaseNote = {
|
const newNote: BaseNote = {
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
content,
|
content,
|
||||||
isRoot: false,
|
isRoot: false,
|
||||||
created: new Date().getTime(),
|
created: new Date().getTime(),
|
||||||
modified: new Date().getTime()
|
modified: new Date().getTime()
|
||||||
}
|
}
|
||||||
baseNotes.value[id] = newNote
|
baseNotes.value[id] = newNote
|
||||||
if (goToNote) setActiveNote(id)
|
if (goToNote) setActiveNote(id)
|
||||||
return newNote
|
return newNote
|
||||||
}
|
}
|
||||||
|
|
||||||
export const deleteNote = (noteId: string) => {
|
export const deleteNote = (noteId: string) => {
|
||||||
const baseNotesClone: BaseNotes = structuredClone(toRaw(baseNotes.value))
|
const baseNotesClone: BaseNotes = structuredClone(toRaw(baseNotes.value))
|
||||||
delete baseNotesClone[noteId]
|
delete baseNotesClone[noteId]
|
||||||
baseNotes.value = baseNotesClone
|
baseNotes.value = baseNotesClone
|
||||||
}
|
}
|
||||||
|
|
||||||
const getNoteLinksByNoteId = (noteId: string): string[] => {
|
const getNoteLinksByNoteId = (noteId: string): string[] => {
|
||||||
const note = baseNotes.value[noteId]
|
const note = baseNotes.value[noteId]
|
||||||
const regex = /\[\[(.*?)\]\]/g
|
const regex = /\[\[(.*?)\]\]/g
|
||||||
const links = getAllMatches(regex, note.content || '')
|
const links = getAllMatches(regex, note.content || '')
|
||||||
.map((to) => notes.value.find((note) => note.title === to[1])?.id || '')
|
.map((to) => notes.value.find((note) => note.title === to[1])?.id || '')
|
||||||
.filter((noteId) => Object.keys(baseNotes.value).includes(noteId))
|
.filter((noteId) => Object.keys(baseNotes.value).includes(noteId))
|
||||||
return [...links]
|
return [...links]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const notesRelations = computed(() => {
|
export const notesRelations = computed(() => {
|
||||||
const noteIds = Object.keys(baseNotes.value)
|
const noteIds = Object.keys(baseNotes.value)
|
||||||
const relations = noteIds
|
const relations = noteIds
|
||||||
.filter((id) => id !== undefined)
|
.filter((id) => id !== undefined)
|
||||||
.map((id) => {
|
.map((id) => {
|
||||||
const to = getNoteLinksByNoteId(id)
|
const to = getNoteLinksByNoteId(id)
|
||||||
return { id, to }
|
return { id, to }
|
||||||
})
|
})
|
||||||
.map((noteRelations, _, notesRelations): NoteRelations => {
|
.map((noteRelations, _, notesRelations): NoteRelations => {
|
||||||
const from = [...notesRelations]
|
const from = [...notesRelations]
|
||||||
.map((noteRelation) =>
|
.map((noteRelation) =>
|
||||||
noteRelation.to
|
noteRelation.to.filter((toId) => toId === noteRelations.id).map(() => noteRelation.id)
|
||||||
.filter((toId) => toId === noteRelations.id)
|
)
|
||||||
.map(() => noteRelation.id)
|
.reduce((arr, elem) => arr.concat(elem), [])
|
||||||
)
|
.filter((value, index, self) => self.indexOf(value) === index)
|
||||||
.reduce((arr, elem) => arr.concat(elem), [])
|
return {
|
||||||
.filter((value, index, self) => self.indexOf(value) === index)
|
id: noteRelations.id,
|
||||||
return {
|
to: noteRelations.to,
|
||||||
id: noteRelations.id,
|
from
|
||||||
to: noteRelations.to,
|
}
|
||||||
from
|
})
|
||||||
}
|
.reduce((notes, { id, to, from }) => {
|
||||||
})
|
notes[id] = { to, from }
|
||||||
.reduce((notes, { id, to, from }) => {
|
return notes
|
||||||
notes[id] = { to, from }
|
}, {} as NotesRelations)
|
||||||
return notes
|
return relations
|
||||||
}, {} as NotesRelations)
|
|
||||||
return relations
|
|
||||||
})
|
})
|
||||||
|
|
||||||
export function getNoteReferences(note: Note) {
|
export function getNoteReferences(note: Note) {
|
||||||
const relations = notesRelations.value[note.id]
|
const relations = notesRelations.value[note.id]
|
||||||
return relations
|
return relations
|
||||||
? (relations.from || [])
|
? (relations.from || [])
|
||||||
.map((noteId) => {
|
.map((noteId) => {
|
||||||
return notes.value.find((note) => note.id === noteId)
|
return notes.value.find((note) => note.id === noteId)
|
||||||
})
|
})
|
||||||
.filter((note): note is Note => note !== undefined)
|
.filter((note): note is Note => note !== undefined)
|
||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
|
|
||||||
const parseBaseNotes = (notes: BaseNotes): BaseNotes => {
|
const parseBaseNotes = (notes: BaseNotes): BaseNotes => {
|
||||||
return Object.fromEntries(
|
return Object.fromEntries(
|
||||||
Object.entries(notes).map(([noteId, note]) => {
|
Object.entries(notes).map(([noteId, note]) => {
|
||||||
return [
|
return [
|
||||||
noteId,
|
noteId,
|
||||||
{
|
{
|
||||||
id: noteId,
|
id: noteId,
|
||||||
title: note.title,
|
title: note.title,
|
||||||
content: note.content,
|
content: note.content,
|
||||||
isRoot: Boolean(note.isRoot),
|
isRoot: Boolean(note.isRoot),
|
||||||
created: note.created || note.modified || new Date().getTime(),
|
created: note.created || note.modified || new Date().getTime(),
|
||||||
modified: note.modified || note.created || new Date().getTime()
|
modified: note.modified || note.created || new Date().getTime()
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getNotes = async () => {
|
export const getNotes = async () => {
|
||||||
baseNotes.value = {}
|
baseNotes.value = {}
|
||||||
let notes: BaseNotes = {}
|
let notes: BaseNotes = {}
|
||||||
if (activeNotesSource.value === 'local') {
|
if (activeNotesSource.value === 'local') {
|
||||||
try {
|
try {
|
||||||
notes = JSON.parse(localStorage.getItem('notes') || '{}')
|
notes = JSON.parse(localStorage.getItem('notes') || '{}')
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log(error)
|
console.log(error)
|
||||||
}
|
|
||||||
} else if (activeNotesSource.value === 'firebase') {
|
|
||||||
if (encryptionKey.value === undefined || !user.value || !db.value) return
|
|
||||||
const firebaseNotes = (
|
|
||||||
await getDoc(doc(db.value, 'pages', user.value.uid))
|
|
||||||
).data() as BaseNotes
|
|
||||||
notes = encryptionKey.value
|
|
||||||
? decryptNotes(firebaseNotes, encryptionKey.value)
|
|
||||||
: firebaseNotes || {}
|
|
||||||
console.log('get notes from firebase', notes)
|
|
||||||
}
|
}
|
||||||
baseNotes.value = parseBaseNotes(notes)
|
} else if (activeNotesSource.value === 'firebase') {
|
||||||
if (!rootNote.value) insertDefaultNotes(defaultNotes)
|
if (encryptionKey.value === undefined || !user.value || !db.value) return
|
||||||
setActiveNote(rootNote.value?.id, false)
|
const firebaseNotes = (await getDoc(doc(db.value, 'pages', user.value.uid))).data() as BaseNotes
|
||||||
|
notes = encryptionKey.value
|
||||||
|
? decryptNotes(firebaseNotes, encryptionKey.value)
|
||||||
|
: firebaseNotes || {}
|
||||||
|
console.log('get notes from firebase', notes)
|
||||||
|
}
|
||||||
|
baseNotes.value = parseBaseNotes(notes)
|
||||||
|
if (!rootNote.value) insertDefaultNotes(defaultNotes)
|
||||||
|
setActiveNote(rootNote.value?.id, false)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import type { notesSourceValues } from '@/composables/useNotes'
|
import type { notesSourceValues } from '@/composables/useNotes'
|
||||||
|
|
||||||
interface Settings {
|
interface Settings {
|
||||||
preferredNotesSource: notesSourceValues | null
|
preferredNotesSource: notesSourceValues | null
|
||||||
}
|
}
|
||||||
|
|
||||||
export const preferredNotesSource = ref<notesSourceValues | null>(null)
|
export const preferredNotesSource = ref<notesSourceValues | null>(null)
|
||||||
|
|
||||||
const updateSettings = () => {
|
const updateSettings = () => {
|
||||||
const settings: Settings = {
|
const settings: Settings = {
|
||||||
preferredNotesSource: preferredNotesSource.value
|
preferredNotesSource: preferredNotesSource.value
|
||||||
}
|
}
|
||||||
localStorage.setItem('settings', JSON.stringify(settings))
|
localStorage.setItem('settings', JSON.stringify(settings))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const initializeSettings = () => {
|
export const initializeSettings = () => {
|
||||||
watch([preferredNotesSource], () => updateSettings())
|
watch([preferredNotesSource], () => updateSettings())
|
||||||
try {
|
try {
|
||||||
const settings: Settings = JSON.parse(localStorage.getItem('settings') || '{}')
|
const settings: Settings = JSON.parse(localStorage.getItem('settings') || '{}')
|
||||||
preferredNotesSource.value = settings.preferredNotesSource
|
preferredNotesSource.value = settings.preferredNotesSource
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
export const viewModes: ViewMode[] = [
|
export const viewModes: ViewMode[] = [
|
||||||
{ name: 'Note', icon: 'fas fa-sticky-note fa-fw' },
|
{ name: 'Note', icon: 'fas fa-sticky-note fa-fw' },
|
||||||
{ name: 'List', icon: 'fas fa-list fa-fw' },
|
{ name: 'List', icon: 'fas fa-list fa-fw' },
|
||||||
{ name: 'Mindmap', icon: 'fas fa-project-diagram fa-fw' }
|
{ name: 'Mindmap', icon: 'fas fa-project-diagram fa-fw' }
|
||||||
]
|
]
|
||||||
export const activeViewMode = ref<ViewMode>(viewModes[0])
|
export const activeViewMode = ref<ViewMode>(viewModes[0])
|
||||||
|
|||||||
@@ -9,14 +9,14 @@ import { StatusBar, Style } from '@capacitor/status-bar'
|
|||||||
import { Keyboard } from '@capacitor/keyboard'
|
import { Keyboard } from '@capacitor/keyboard'
|
||||||
|
|
||||||
if (Capacitor.isNativePlatform()) {
|
if (Capacitor.isNativePlatform()) {
|
||||||
StatusBar.setStyle({ style: Style.Dark })
|
StatusBar.setStyle({ style: Style.Dark })
|
||||||
Keyboard.setAccessoryBarVisible({ isVisible: true })
|
Keyboard.setAccessoryBarVisible({ isVisible: true })
|
||||||
}
|
}
|
||||||
initializeFirebase()
|
initializeFirebase()
|
||||||
|
|
||||||
const isDark = usePreferredDark()
|
const isDark = usePreferredDark()
|
||||||
const favicon = computed<string>(() =>
|
const favicon = computed<string>(() =>
|
||||||
isDark.value ? '/contexted-white.ico' : '/contexted-black.ico'
|
isDark.value ? '/contexted-white.ico' : '/contexted-black.ico'
|
||||||
)
|
)
|
||||||
useFavicon(favicon)
|
useFavicon(favicon)
|
||||||
|
|
||||||
|
|||||||
136
src/style.scss
@@ -5,92 +5,92 @@
|
|||||||
@import '@fontsource/source-sans-pro/300';
|
@import '@fontsource/source-sans-pro/300';
|
||||||
|
|
||||||
* {
|
* {
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
html {
|
html {
|
||||||
// height: -webkit-fill-available;
|
// height: -webkit-fill-available;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
// min-height: 100vh;
|
// min-height: 100vh;
|
||||||
// min-height: -webkit-fill-available;
|
// min-height: -webkit-fill-available;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
min-height: 100%;
|
min-height: 100%;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
transform: translateZ(0);
|
transform: translateZ(0);
|
||||||
touch-action: manipulation;
|
touch-action: manipulation;
|
||||||
-webkit-user-drag: none;
|
-webkit-user-drag: none;
|
||||||
-ms-content-zooming: none;
|
-ms-content-zooming: none;
|
||||||
font-family: 'Source Sans Pro', sans-serif;
|
font-family: 'Source Sans Pro', sans-serif;
|
||||||
// overflow-y: scroll;
|
// overflow-y: scroll;
|
||||||
@apply flex flex-col bg-gray-100;
|
@apply flex flex-col bg-gray-100;
|
||||||
#app {
|
#app {
|
||||||
@apply absolute bottom-0 left-0 right-0 top-0 flex flex-grow flex-col overflow-hidden;
|
@apply absolute bottom-0 left-0 right-0 top-0 flex flex-grow flex-col overflow-hidden;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p:not(:last-child) {
|
p:not(:last-child) {
|
||||||
@apply mb-2;
|
@apply mb-2;
|
||||||
margin-top: 0 !important;
|
margin-top: 0 !important;
|
||||||
}
|
}
|
||||||
p:last-child {
|
p:last-child {
|
||||||
@apply my-0;
|
@apply my-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ck-body-wrapper {
|
.ck-body-wrapper {
|
||||||
height: 0;
|
height: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ck-content {
|
.ck-content {
|
||||||
padding: 0 !important;
|
padding: 0 !important;
|
||||||
border: 0 !important;
|
border: 0 !important;
|
||||||
outline: none !important;
|
outline: none !important;
|
||||||
box-shadow: none !important;
|
box-shadow: none !important;
|
||||||
line-height: 2rem;
|
line-height: 2rem;
|
||||||
a[data-contexted-link='true'] {
|
a[data-contexted-link='true'] {
|
||||||
@apply cursor-pointer font-semibold text-primary hover:bg-gray-200;
|
@apply cursor-pointer font-semibold text-primary hover:bg-gray-200;
|
||||||
}
|
}
|
||||||
&.ck.ck-editor__editable_inline > * {
|
&.ck.ck-editor__editable_inline > * {
|
||||||
margin: 0 !important;
|
margin: 0 !important;
|
||||||
}
|
}
|
||||||
ol,
|
ol,
|
||||||
ul {
|
ul {
|
||||||
@apply my-2 ps-8;
|
@apply my-2 ps-8;
|
||||||
}
|
}
|
||||||
ol {
|
ol {
|
||||||
@apply list-decimal;
|
@apply list-decimal;
|
||||||
}
|
}
|
||||||
ul {
|
ul {
|
||||||
@apply list-disc;
|
@apply list-disc;
|
||||||
}
|
}
|
||||||
h1,
|
h1,
|
||||||
h2,
|
h2,
|
||||||
h3,
|
h3,
|
||||||
h4 {
|
h4 {
|
||||||
@apply font-semibold;
|
@apply font-semibold;
|
||||||
}
|
}
|
||||||
h1 {
|
h1 {
|
||||||
@apply text-3xl;
|
@apply text-3xl;
|
||||||
}
|
}
|
||||||
h2 {
|
h2 {
|
||||||
@apply text-2xl;
|
@apply text-2xl;
|
||||||
}
|
}
|
||||||
h3 {
|
h3 {
|
||||||
@apply text-xl;
|
@apply text-xl;
|
||||||
}
|
}
|
||||||
h4 {
|
h4 {
|
||||||
@apply text-lg;
|
@apply text-lg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--safe-area-top: env(safe-area-inset-top);
|
--safe-area-top: env(safe-area-inset-top);
|
||||||
--safe-area-right: env(safe-area-inset-right);
|
--safe-area-right: env(safe-area-inset-right);
|
||||||
--safe-area-bottom: env(safe-area-inset-bottom);
|
--safe-area-bottom: env(safe-area-inset-bottom);
|
||||||
--safe-area-left: env(safe-area-inset-left);
|
--safe-area-left: env(safe-area-inset-left);
|
||||||
}
|
}
|
||||||
|
|||||||
74
src/types.d.ts
vendored
@@ -1,48 +1,48 @@
|
|||||||
declare global {
|
declare global {
|
||||||
interface BaseNote {
|
interface BaseNote {
|
||||||
id: string
|
id: string
|
||||||
title: string
|
title: string
|
||||||
content: string
|
content: string
|
||||||
created: number
|
created: number
|
||||||
modified: number
|
modified: number
|
||||||
isRoot: boolean
|
isRoot: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Note extends BaseNote {
|
interface Note extends BaseNote {
|
||||||
wordCount: number
|
wordCount: number
|
||||||
}
|
}
|
||||||
|
|
||||||
interface BaseNotes {
|
interface BaseNotes {
|
||||||
[noteId: string]: BaseNote
|
[noteId: string]: BaseNote
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ViewMode {
|
interface ViewMode {
|
||||||
name: string
|
name: string
|
||||||
icon: string
|
icon: string
|
||||||
}
|
}
|
||||||
|
|
||||||
interface AutocompleteEvent {
|
interface AutocompleteEvent {
|
||||||
position?: any
|
position?: any
|
||||||
autocompleteText?: string
|
autocompleteText?: string
|
||||||
domElement?: HTMLElement
|
domElement?: HTMLElement
|
||||||
show: boolean
|
show: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NoteRelations {
|
interface NoteRelations {
|
||||||
id: string
|
id: string
|
||||||
to: string[]
|
to: string[]
|
||||||
from: string[]
|
from: string[]
|
||||||
}
|
}
|
||||||
|
|
||||||
interface NotesRelations {
|
interface NotesRelations {
|
||||||
[noteId: string]: {
|
[noteId: string]: {
|
||||||
to: string[]
|
to: string[]
|
||||||
from: string[]
|
from: string[]
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type ClientKey = string
|
type ClientKey = string
|
||||||
type EncryptedEncryptionKey = string
|
type EncryptedEncryptionKey = string
|
||||||
type EncryptionKey = string
|
type EncryptionKey = string
|
||||||
}
|
}
|
||||||
export {}
|
export {}
|
||||||
|
|||||||
@@ -4,20 +4,20 @@ const encryptionPrefix = 'contexted|'
|
|||||||
const salt = 'salt'
|
const salt = 'salt'
|
||||||
|
|
||||||
export const calculateClientKey = (passphrase: string): ClientKey => {
|
export const calculateClientKey = (passphrase: string): ClientKey => {
|
||||||
return CryptoJS.PBKDF2(passphrase, salt, { keySize: 256 / 32 }).toString()
|
return CryptoJS.PBKDF2(passphrase, salt, { keySize: 256 / 32 }).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decrypt = (encryptedMessage: string, key: string): string => {
|
export const decrypt = (encryptedMessage: string, key: string): string => {
|
||||||
const decryptedMessage = CryptoJS.AES.decrypt(encryptedMessage, key).toString(CryptoJS.enc.Utf8)
|
const decryptedMessage = CryptoJS.AES.decrypt(encryptedMessage, key).toString(CryptoJS.enc.Utf8)
|
||||||
if (!decryptedMessage.startsWith(encryptionPrefix))
|
if (!decryptedMessage.startsWith(encryptionPrefix))
|
||||||
throw new Error("Message doesn't have valid encryption")
|
throw new Error("Message doesn't have valid encryption")
|
||||||
return decryptedMessage.substring(encryptionPrefix.length)
|
return decryptedMessage.substring(encryptionPrefix.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const encrypt = (unencryptedMessage: string, key: string): string => {
|
export const encrypt = (unencryptedMessage: string, key: string): string => {
|
||||||
return CryptoJS.AES.encrypt(encryptionPrefix + unencryptedMessage, key).toString()
|
return CryptoJS.AES.encrypt(encryptionPrefix + unencryptedMessage, key).toString()
|
||||||
}
|
}
|
||||||
|
|
||||||
export const generateEncryptionKey = () => {
|
export const generateEncryptionKey = () => {
|
||||||
return CryptoJS.lib.WordArray.random(16).toString(CryptoJS.enc.Hex)
|
return CryptoJS.lib.WordArray.random(16).toString(CryptoJS.enc.Hex)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,24 @@
|
|||||||
import shortid from 'shortid'
|
import shortid from 'shortid'
|
||||||
|
|
||||||
export const defaultNotes: BaseNote[] = [
|
export const defaultNotes: BaseNote[] = [
|
||||||
{
|
{
|
||||||
isRoot: true,
|
isRoot: true,
|
||||||
title: 'Your first note',
|
title: 'Your first note',
|
||||||
content: `Contexted is a **relational note-taking app**. Use it as your personal knowledge base, research assistent or just to map out thoughts.\n\n
|
content: `Contexted is a **relational note-taking app**. Use it as your personal knowledge base, research assistent or just to map out thoughts.\n\n
|
||||||
# How does it work?
|
# How does it work?
|
||||||
* Create a new note by typing words between [[brackets]]
|
* Create a new note by typing words between [[brackets]]
|
||||||
* Click on **Mindmap mode** in the menu left to visualize your notes
|
* Click on **Mindmap mode** in the menu left to visualize your notes
|
||||||
\n
|
\n
|
||||||
Let's get started!`
|
Let's get started!`
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'brackets',
|
title: 'brackets',
|
||||||
content: `If you type square brackets around text a link is created automatically. Like magic!`,
|
content: `If you type square brackets around text a link is created automatically. Like magic!`,
|
||||||
isRoot: false
|
isRoot: false
|
||||||
}
|
}
|
||||||
].map((note) => ({
|
].map((note) => ({
|
||||||
id: shortid.generate(),
|
id: shortid.generate(),
|
||||||
created: new Date().getTime(),
|
created: new Date().getTime(),
|
||||||
modified: new Date().getTime(),
|
modified: new Date().getTime(),
|
||||||
...note
|
...note
|
||||||
}))
|
}))
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import { formatDistance, format, isToday } from 'date-fns'
|
import { formatDistance, format, isToday } from 'date-fns'
|
||||||
|
|
||||||
export const formatDate = (date: Date | number): string => {
|
export const formatDate = (date: Date | number): string => {
|
||||||
const dateToFormat = ['number', 'string'].includes(typeof date) ? new Date(date) : date
|
const dateToFormat = ['number', 'string'].includes(typeof date) ? new Date(date) : date
|
||||||
const dateDistanceInWords = formatDistance(dateToFormat, new Date()) + ' ago'
|
const dateDistanceInWords = formatDistance(dateToFormat, new Date()) + ' ago'
|
||||||
return isToday(date) ? dateDistanceInWords : format(date, "d MMMM 'at' HH:mm ")
|
return isToday(date) ? dateDistanceInWords : format(date, "d MMMM 'at' HH:mm ")
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAllMatches = (regex: RegExp, input: string): RegExpExecArray[] => {
|
export const getAllMatches = (regex: RegExp, input: string): RegExpExecArray[] => {
|
||||||
const matches = []
|
const matches = []
|
||||||
let m
|
let m
|
||||||
do {
|
do {
|
||||||
m = regex.exec(input)
|
m = regex.exec(input)
|
||||||
// console.log(m)
|
// console.log(m)
|
||||||
if (m) matches.push(m)
|
if (m) matches.push(m)
|
||||||
} while (m)
|
} while (m)
|
||||||
return matches
|
return matches
|
||||||
}
|
}
|
||||||
|
|
||||||
export const windowIsMobile = () => window.innerWidth < 640
|
export const windowIsMobile = () => window.innerWidth < 640
|
||||||
|
|||||||
@@ -3,40 +3,40 @@ import DOMPurify from 'dompurify'
|
|||||||
import Turndown from 'turndown'
|
import Turndown from 'turndown'
|
||||||
|
|
||||||
export function mdToHtml(mdText: string): string {
|
export function mdToHtml(mdText: string): string {
|
||||||
const renderer = new marked.Renderer()
|
const renderer = new marked.Renderer()
|
||||||
|
|
||||||
const html = DOMPurify.sanitize(marked.parse(mdText, { renderer }))
|
const html = DOMPurify.sanitize(marked.parse(mdText, { renderer }))
|
||||||
const re = /(\[\[)(.*?)(\]\])/g
|
const re = /(\[\[)(.*?)(\]\])/g
|
||||||
const doc = new DOMParser().parseFromString(html, 'text/html')
|
const doc = new DOMParser().parseFromString(html, 'text/html')
|
||||||
doc.querySelectorAll('p, b, u, i, li, h1, h2, h3').forEach((element) => {
|
doc.querySelectorAll('p, b, u, i, li, h1, h2, h3').forEach((element) => {
|
||||||
element.innerHTML = element.innerHTML.replace(re, (_, p1, p2, p3) => {
|
element.innerHTML = element.innerHTML.replace(re, (_, p1, p2, p3) => {
|
||||||
return `${p1}<a data-contexted-link="true">${p2}</a>${p3}`
|
return `${p1}<a data-contexted-link="true">${p2}</a>${p3}`
|
||||||
})
|
|
||||||
})
|
})
|
||||||
return doc.body.innerHTML
|
})
|
||||||
|
return doc.body.innerHTML
|
||||||
}
|
}
|
||||||
|
|
||||||
export function htmlToMd(htmlText: string): string {
|
export function htmlToMd(htmlText: string): string {
|
||||||
const turndown = new Turndown({ headingStyle: 'atx' })
|
const turndown = new Turndown({ headingStyle: 'atx' })
|
||||||
const escapes = [
|
const escapes = [
|
||||||
[/\\/g, '\\\\'],
|
[/\\/g, '\\\\'],
|
||||||
[/\*/g, '\\*'],
|
[/\*/g, '\\*'],
|
||||||
[/^-/g, '\\-'],
|
[/^-/g, '\\-'],
|
||||||
[/^\+ /g, '\\+ '],
|
[/^\+ /g, '\\+ '],
|
||||||
[/^(=+)/g, '\\$1'],
|
[/^(=+)/g, '\\$1'],
|
||||||
[/^(#{1,6}) /g, '\\$1 '],
|
[/^(#{1,6}) /g, '\\$1 '],
|
||||||
[/`/g, '\\`'],
|
[/`/g, '\\`'],
|
||||||
[/^~~~/g, '\\~~~'],
|
[/^~~~/g, '\\~~~'],
|
||||||
// [/\[/g, '\\['],
|
// [/\[/g, '\\['],
|
||||||
// [/\]/g, '\\]'],
|
// [/\]/g, '\\]'],
|
||||||
[/^>/g, '\\>'],
|
[/^>/g, '\\>'],
|
||||||
[/_/g, '\\_'],
|
[/_/g, '\\_'],
|
||||||
[/^(\d+)\. /g, '$1\\. ']
|
[/^(\d+)\. /g, '$1\\. ']
|
||||||
]
|
]
|
||||||
turndown.escape = (string) =>
|
turndown.escape = (string) =>
|
||||||
escapes.reduce((accumulator, escape) => {
|
escapes.reduce((accumulator, escape) => {
|
||||||
return accumulator.replace(escape[0], escape[1] as string)
|
return accumulator.replace(escape[0], escape[1] as string)
|
||||||
}, string)
|
}, string)
|
||||||
const md = turndown.turndown(htmlText)
|
const md = turndown.turndown(htmlText)
|
||||||
return md
|
return md
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,12 +38,12 @@ export default {
|
|||||||
themes: [
|
themes: [
|
||||||
{
|
{
|
||||||
contexted: {
|
contexted: {
|
||||||
...require('daisyui/src/theming/themes')['light'],
|
...require('daisyui/src/theming/themes')['[data-theme=light]'],
|
||||||
primary,
|
primary,
|
||||||
secondary,
|
secondary,
|
||||||
'--btn-text-case': 'uppercase', // set default text transform for buttons
|
'--btn-text-case': 'uppercase' // set default text transform for buttons
|
||||||
// accent: '#37CDBE',
|
// accent: '#37CDBE',
|
||||||
neutral: primary,
|
// neutral: '#F7F7F7',
|
||||||
// 'base-100': '#FFFFFF',
|
// 'base-100': '#FFFFFF',
|
||||||
// info: '#3ABFF8',
|
// info: '#3ABFF8',
|
||||||
// success: '#36D399',
|
// success: '#36D399',
|
||||||
|
|||||||