25 Commits

Author SHA1 Message Date
Eric Tuvesson
94dd3dbf0e chore: bump version (#62) 2024-08-08 21:27:12 +02:00
Eric Tuvesson
c593a134b3 chore: rename project name (#60) 2024-08-08 21:23:23 +02:00
Eric Tuvesson
dc638ea8fc feat: Upgrade Electron (#61) 2024-08-08 21:12:39 +02:00
Eric Tuvesson
759c8a0030 fix(runtime): Column node, hide children on calculation (#59)
This removes the initial visible layout shift
2024-07-24 15:59:10 +02:00
Eric Tuvesson
aea80c6586 feat(runtime): Add capture attribute on Open File Picker node (#57)
- https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#capture
- https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/capture
2024-07-19 15:03:03 +02:00
Eric Tuvesson
46e2efa576 feat(runtime): Add "Switched" signal output on the Switch node (#58) 2024-07-19 15:02:49 +02:00
Eric Tuvesson
c508e15546 chore: clean up user nodes (#53) 2024-07-15 15:21:01 +02:00
Eric Tuvesson
d61effc615 feat(editor): Deploy popup always mounted (#51)
This removes the delayed loading when opening the deploy popup
2024-07-08 12:47:28 +02:00
Eric Tuvesson
12be6dc69f feat(editor): Upgrade electron (#50) 2024-07-08 09:35:18 +02:00
Eric Tuvesson
17e3d16436 chore: clean up editor assets (#12) 2024-07-08 08:09:39 +02:00
Eric Tuvesson
233479a1bc feat: Add Support for Parse Server v7 (#20)
* feat: Upgrade Aggregate queries to latest Parse API

* feat: Save parse server major version in metadata

This can be used to determine which version of the Parse API that will be used.

* fix: Add support for both versions of aggregate queries
2024-07-08 08:06:47 +02:00
Eric Tuvesson
57e5246022 feat(runtime): Add default label to String Format node (#49)
Update the String Format node default label to show the format, like how the other nodes work.
2024-07-04 15:54:20 +02:00
Eric Tuvesson
dda22e0de6 feat(runtime): Add Image node output "On Error" signal (#48)
"On Error" is triggered when the image is not loading correctly, making it possible to hide the Image node or show a placeholder instead.
2024-07-04 14:02:34 +02:00
Eric Tuvesson
618955e1ee feat: Portal include Project ID (#47) 2024-06-28 00:43:34 +02:00
Eric Tuvesson
8c7d4faeca fix(runtime): Column node added a div with empty Repeater (#45)
When the Column node only had an empty Repeater child, there was an empty HTML element.
2024-06-27 21:16:50 +02:00
Eric Tuvesson
c5754c9160 feat(runtime): Add "data-testId" attribute to Columns node (#44) 2024-06-27 15:42:25 +02:00
Eric Tuvesson
3fb3668fc3 feat: Allow relative git repository (#41) 2024-06-26 21:11:50 +02:00
Eric Tuvesson
fa282d6169 feat(runtime): Add "data-testid" attributes to UI nodes (#42) 2024-06-26 21:08:01 +02:00
Eric Tuvesson
0ee55c26eb feat(editor): Search panel show "(Cloud Function)" if result is in Cloud Functions sheet (#40) 2024-06-17 10:35:02 +02:00
Eric Tuvesson
3a31b86d48 chore(runtime): Clean up "Push Component To Stack" (#30) 2024-06-13 22:25:16 +02:00
Eric Tuvesson
2f06952e4a fix(runtime): JavaScript Records API error handling (#32) 2024-06-13 22:24:34 +02:00
Eric Tuvesson
44a40aef96 fix: Add more error handling to JavaScript Records API (#33) 2024-06-13 22:24:05 +02:00
Eric Tuvesson
0a69765460 chore: Update template provider name (#38) 2024-06-13 22:23:30 +02:00
alan-x-n
2ebd57b29a chore: Update README.md 2024-06-02 15:29:25 -07:00
alan-x-n
5225d26870 chore: Updated README.md 2024-06-02 15:27:35 -07:00
66 changed files with 838 additions and 580 deletions

View File

@@ -1,15 +1,15 @@
name: Build noodl-editor
name: Build fluxscape-editor
on:
# Allows you to run this workflow manually from the Actions tab
workflow_dispatch:
# Allows you to run this workflow from another workflow
workflow_call:
# release:
# types: [created]
jobs:
build_noodl_editor:
runs-on: ${{ matrix.os }}
@@ -32,11 +32,11 @@ jobs:
platform: linux-x64
steps:
- if: ${{ matrix.platform == 'darwin-arm64' }}
- if: ${{ matrix.platform == 'darwin-arm64' }}
name: Setup
uses: actions/setup-python@v5
with:
python-version: '3.11'
python-version: '3.11'
- name: Checkout
uses: actions/checkout@v4
@@ -72,6 +72,6 @@ jobs:
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: noodl-editor-${{ matrix.platform }}-${{ github.head_ref }}-${{ github.sha }}
name: fluxscape-editor-${{ matrix.platform }}-${{ github.head_ref }}-${{ github.sha }}
path: publish
retention-days: "12"
retention-days: '12'

View File

@@ -1,4 +1,4 @@
name: Test noodl-editor
name: Test fluxscape-editor
on:
# Allows you to run this workflow manually from the Actions tab

View File

@@ -1,21 +1,21 @@
# Noodl
# Fluxscape
[Noodl](https://noodl.net) is a low-code platform where designers and developers build custom applications and experiences. Designed as a visual programming environment, it aims to expedite your development process. It promotes the swift and efficient creation of applications, requiring minimal coding knowledge.
Fluxscape is a low-code platform where designers and developers build custom applications and experiences. Designed as a visual programming environment, it aims to expedite your development process. It promotes the swift and efficient creation of applications, requiring minimal coding knowledge.
## Documentation
Documentation for how to use Noodl can be found here:
[https://noodlapp.github.io/noodl-docs/](https://noodlapp.github.io/noodl-docs/)
Documentation for how to use Fluxscape can be found here:
[Fluxscape Documentation](https://docs.fluxscape.io)
## Community
Main support channel is Discord: [https://www.noodl.net/community](https://www.noodl.net/community)
Main support channel is Discord: [Fluxscape Discord](https://discord.gg/fXNW9EXa6A)
## Download releases
Pre-built binaries can be [downloaded from Github](https://github.com/noodlapp/noodl/releases)
Pre-built binaries can be [downloaded from Github](https://github.com/fluxscape/fluxscape/releases)
## Note for users who are migrating from the deprecated closed source version
- [Migrating the project files and workspaces to a Git provider](https://noodlapp.github.io/noodl-docs/docs/guides/collaboration/migrating-from-noodl-hosted-git)
- [Migrate backend and database](https://noodlapp.github.io/noodl-docs/docs/guides/deploy/using-an-external-backend#migrating-from-a-noodl-cloud-service)
- [Self-host frontend](https://noodlapp.github.io/noodl-docs/docs/guides/deploy/hosting-frontend)
- [Migrating the project files and workspaces to a Git provider](https://docs.fluxscape.io/docs/guides/collaboration/migrating-from-noodl-hosted-git/)
- [Migrate backend and database](https://docs.fluxscape.io/docs/guides/deploy/using-an-external-backend/#migrating-from-a-noodl-cloud-service)
- [Self-host frontend](https://docs.fluxscape.io/docs/guides/deploy/hosting-frontend/)
## Building from source
@@ -23,24 +23,24 @@ Pre-built binaries can be [downloaded from Github](https://github.com/noodlapp/n
# Install all dependencies
$ npm install
# Start the Noodl Editor and build a production version of the cloud and react runtime (useful when running Noodl from source but want to deploy to production)
# Start the Fluxscape Editor and build a production version of the cloud and react runtime (useful when running Fluxscape from source but want to deploy to production)
$ npm start
# Start the Noodl Editor and watch the filesystem for changes to the runtimes. Development versions of the runtimes, not meant for production (mostly due to source maps and file size)
# Start the Fluxscape Editor and watch the filesystem for changes to the runtimes. Development versions of the runtimes, not meant for production (mostly due to source maps and file size)
# This is ideal for a quick workflow when doing changes on the runtimes.
$ npm run dev
# Start Noodl Editor test runner
# Start Fluxscape Editor test runner
$ npm run test:editor
```
## Licenses
This repository contains two different licenses for different parts of the Noodl platform.
This repository contains two different licenses for different parts of the Fluxscape platform.
- Components related to the editor, used to edit Noodl projects, are under GPLv3
- Components related to the end applications, used by the applications Noodl deploys, are under MIT
- Components related to the editor, used to edit Fluxscape projects, are under GPLv3
- Components related to the end applications, used by the applications Fluxscape deploys, are under MIT
All of the source code of applications created with Noodl are under MIT. This means you can do project specific changes to the runtime without having to redistribute your changes.
All of the source code of applications created with Fluxscape are under MIT. This means you can do project specific changes to the runtime without having to redistribute your changes.
Packaged licensed under MIT:
- `noodl-runtime`

347
package-lock.json generated
View File

@@ -2849,9 +2849,9 @@
}
},
"node_modules/@electron/remote": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.1.tgz",
"integrity": "sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz",
"integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==",
"peerDependencies": {
"electron": ">= 13.0.0"
}
@@ -25220,13 +25220,13 @@
}
},
"node_modules/electron": {
"version": "28.1.4",
"resolved": "https://registry.npmjs.org/electron/-/electron-28.1.4.tgz",
"integrity": "sha512-WE6go611KOhtH6efRPMnVC7FE7DCKnQ3ZyHFeI1DbaCy8OU4UjZ8/CZGcuZmZgRdxSBEHoHdgaJkWRHZzF0FOg==",
"version": "31.3.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-31.3.1.tgz",
"integrity": "sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==",
"hasInstallScript": true,
"dependencies": {
"@electron/get": "^2.0.0",
"@types/node": "^18.11.18",
"@types/node": "^20.9.0",
"extract-zip": "^2.0.1"
},
"bin": {
@@ -25452,6 +25452,14 @@
"tiny-typed-emitter": "^2.1.0"
}
},
"node_modules/electron/node_modules/@types/node": {
"version": "20.14.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
"dependencies": {
"undici-types": "~5.26.4"
}
},
"node_modules/elliptic": {
"version": "6.5.4",
"dev": true,
@@ -27203,6 +27211,10 @@
"react": "^15.0.2 || ^16.0.0 || ^17.0.0"
}
},
"node_modules/fluxscape-editor": {
"resolved": "packages/noodl-editor",
"link": true
},
"node_modules/focus-lock": {
"version": "0.8.1",
"dev": true,
@@ -35443,10 +35455,6 @@
"dev": true,
"license": "MIT"
},
"node_modules/noodl-editor": {
"resolved": "packages/noodl-editor",
"link": true
},
"node_modules/nopt": {
"version": "1.0.10",
"license": "MIT",
@@ -43191,6 +43199,11 @@
"version": "1.13.6",
"license": "MIT"
},
"node_modules/undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"node_modules/unfetch": {
"version": "4.2.0",
"dev": true,
@@ -48967,9 +48980,10 @@
"dev": true
},
"packages/noodl-editor": {
"version": "1.0.0",
"name": "fluxscape-editor",
"version": "1.1.0",
"dependencies": {
"@electron/remote": "^2.1.1",
"@electron/remote": "^2.1.2",
"@jaames/iro": "^5.5.2",
"@microsoft/fetch-event-source": "^2.0.1",
"@noodl/git": "file:../noodl-git",
@@ -49025,7 +49039,7 @@
"babel-loader": "^8.2.4",
"concurrently": "^7.4.0",
"css-loader": "^6.7.1",
"electron": "28.1.4",
"electron": "31.3.1",
"electron-builder": "^24.9.1",
"file-loader": "^6.2.0",
"html-loader": "^3.1.0",
@@ -53375,9 +53389,9 @@
}
},
"@electron/remote": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.1.tgz",
"integrity": "sha512-Lfxul2yBxL+FBVaKszNAkuUqSIDbUQ1I7BC394iRXyqA2XGz7im2bAxroNIM51jhySSPKUaOLHaFLxfV6pC9VQ==",
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@electron/remote/-/remote-2.1.2.tgz",
"integrity": "sha512-EPwNx+nhdrTBxyCqXt/pftoQg/ybtWDW3DUWHafejvnB1ZGGfMpv6e15D8KeempocjXe78T7WreyGGb3mlZxdA==",
"requires": {}
},
"@electron/universal": {
@@ -74057,13 +74071,23 @@
}
},
"electron": {
"version": "28.1.4",
"resolved": "https://registry.npmjs.org/electron/-/electron-28.1.4.tgz",
"integrity": "sha512-WE6go611KOhtH6efRPMnVC7FE7DCKnQ3ZyHFeI1DbaCy8OU4UjZ8/CZGcuZmZgRdxSBEHoHdgaJkWRHZzF0FOg==",
"version": "31.3.1",
"resolved": "https://registry.npmjs.org/electron/-/electron-31.3.1.tgz",
"integrity": "sha512-9fiuWlRhBfygtcT+auRd/WdBK/f8LZZcrpx0RjpXhH2DPTP/PfnkC4JB1PW55qCbGbh4wAgkYbf4ExIag8oGCA==",
"requires": {
"@electron/get": "^2.0.0",
"@types/node": "^18.11.18",
"@types/node": "^20.9.0",
"extract-zip": "^2.0.1"
},
"dependencies": {
"@types/node": {
"version": "20.14.10",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.10.tgz",
"integrity": "sha512-MdiXf+nDuMvY0gJKxyfZ7/6UFsETO7mGKF54MVD/ekJS6HdFtpZFBgrh6Pseu64XTb2MLyFPlbW6hj8HYRQNOQ==",
"requires": {
"undici-types": "~5.26.4"
}
}
}
},
"electron-builder": {
@@ -75408,6 +75432,144 @@
"fbjs": "^3.0.1"
}
},
"fluxscape-editor": {
"version": "file:packages/noodl-editor",
"requires": {
"@babel/core": "^7.19.1",
"@babel/preset-react": "^7.18.6",
"@electron/remote": "^2.1.2",
"@jaames/iro": "^5.5.2",
"@microsoft/fetch-event-source": "^2.0.1",
"@noodl/git": "file:../noodl-git",
"@noodl/noodl-parse-dashboard": "file:../noodl-parse-dashboard",
"@noodl/platform": "file:../noodl-platform",
"@noodl/platform-electron": "file:../noodl-platform-electron",
"@svgr/webpack": "^6.4.0",
"@types/checksum": "^0.1.33",
"@types/jasmine": "^4.3.0",
"@types/jquery": "^3.5.14",
"@types/react": "^17.0.50",
"@types/react-dom": "^18.0.0",
"@types/remarkable": "^2.0.3",
"@types/rimraf": "^3.0.2",
"@types/split2": "^3.2.1",
"@types/string.prototype.matchall": "^4.0.1",
"@types/underscore": "^1.11.4",
"@types/webpack-env": "^1.18.0",
"about-window": "^1.15.2",
"algoliasearch": "^4.14.2",
"archiver": "^5.3.0",
"async": "^3.2.4",
"babel-loader": "^8.2.4",
"classnames": "^2.3.2",
"concurrently": "^7.4.0",
"css-loader": "^6.7.1",
"diff3": "0.0.4",
"dmg-license": "^1.0.11",
"electron": "31.3.1",
"electron-builder": "^24.9.1",
"electron-store": "^8.1.0",
"electron-updater": "^6.1.7",
"express": "^4.17.3",
"file-loader": "^6.2.0",
"highlight.js": "^11.5.1",
"html-loader": "^3.1.0",
"isbinaryfile": "^5.0.0",
"md5": "^2.3.0",
"md5-file": "^5.0.0",
"mixpanel-browser": "^2.45.0",
"mkdirp": "0.5.1",
"mkdirp-sync": "0.0.2",
"monaco-editor": "^0.34.0",
"monaco-editor-webpack-plugin": "^7.0.1",
"ncp": "^2.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.0",
"react-hot-toast": "^2.4.0",
"react-instantsearch-hooks-web": "^6.38.0",
"react-json-view": "^1.21.3",
"react-rnd": "^10.3.7",
"remarkable": "^2.0.1",
"rimraf": "^3.0.2",
"s3": "git+https://github.com/noodlapp/node-s3-client.git",
"sass": "^1.55.0",
"sass-loader": "^12.6.0",
"string.prototype.matchall": "^4.0.7",
"stringify": "^5.2.0",
"style-loader": "^3.3.1",
"ts-loader": "^9.4.1",
"ts-node": "^10.7.0",
"typescript": "^4.8.3",
"underscore": "^1.13.6",
"url-loader": "^4.1.1",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1",
"webpack-merge": "^5.8.0",
"websocket-stream": "^5.5.2",
"ws": "^8.9.0"
},
"dependencies": {
"@webpack-cli/configtest": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
"integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
"dev": true,
"requires": {}
},
"@webpack-cli/info": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
"integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
"dev": true,
"requires": {
"envinfo": "^7.7.3"
}
},
"@webpack-cli/serve": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
"integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
"dev": true,
"requires": {}
},
"commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"dev": true
},
"rechoir": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
"integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
"dev": true,
"requires": {
"resolve": "^1.9.0"
}
},
"webpack-cli": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
"integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
"dev": true,
"requires": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^1.2.0",
"@webpack-cli/info": "^1.5.0",
"@webpack-cli/serve": "^1.7.0",
"colorette": "^2.0.14",
"commander": "^7.0.0",
"cross-spawn": "^7.0.3",
"fastest-levenshtein": "^1.0.12",
"import-local": "^3.0.2",
"interpret": "^2.2.0",
"rechoir": "^0.7.0",
"webpack-merge": "^5.7.3"
}
}
}
},
"focus-lock": {
"version": "0.8.1",
"dev": true,
@@ -80983,144 +81145,6 @@
"version": "2.0.10",
"dev": true
},
"noodl-editor": {
"version": "file:packages/noodl-editor",
"requires": {
"@babel/core": "^7.19.1",
"@babel/preset-react": "^7.18.6",
"@electron/remote": "^2.1.1",
"@jaames/iro": "^5.5.2",
"@microsoft/fetch-event-source": "^2.0.1",
"@noodl/git": "file:../noodl-git",
"@noodl/noodl-parse-dashboard": "file:../noodl-parse-dashboard",
"@noodl/platform": "file:../noodl-platform",
"@noodl/platform-electron": "file:../noodl-platform-electron",
"@svgr/webpack": "^6.4.0",
"@types/checksum": "^0.1.33",
"@types/jasmine": "^4.3.0",
"@types/jquery": "^3.5.14",
"@types/react": "^17.0.50",
"@types/react-dom": "^18.0.0",
"@types/remarkable": "^2.0.3",
"@types/rimraf": "^3.0.2",
"@types/split2": "^3.2.1",
"@types/string.prototype.matchall": "^4.0.1",
"@types/underscore": "^1.11.4",
"@types/webpack-env": "^1.18.0",
"about-window": "^1.15.2",
"algoliasearch": "^4.14.2",
"archiver": "^5.3.0",
"async": "^3.2.4",
"babel-loader": "^8.2.4",
"classnames": "^2.3.2",
"concurrently": "^7.4.0",
"css-loader": "^6.7.1",
"diff3": "0.0.4",
"dmg-license": "^1.0.11",
"electron": "28.1.4",
"electron-builder": "^24.9.1",
"electron-store": "^8.1.0",
"electron-updater": "^6.1.7",
"express": "^4.17.3",
"file-loader": "^6.2.0",
"highlight.js": "^11.5.1",
"html-loader": "^3.1.0",
"isbinaryfile": "^5.0.0",
"md5": "^2.3.0",
"md5-file": "^5.0.0",
"mixpanel-browser": "^2.45.0",
"mkdirp": "0.5.1",
"mkdirp-sync": "0.0.2",
"monaco-editor": "^0.34.0",
"monaco-editor-webpack-plugin": "^7.0.1",
"ncp": "^2.0.0",
"react": "^17.0.2",
"react-dom": "^17.0.0",
"react-hot-toast": "^2.4.0",
"react-instantsearch-hooks-web": "^6.38.0",
"react-json-view": "^1.21.3",
"react-rnd": "^10.3.7",
"remarkable": "^2.0.1",
"rimraf": "^3.0.2",
"s3": "git+https://github.com/noodlapp/node-s3-client.git",
"sass": "^1.55.0",
"sass-loader": "^12.6.0",
"string.prototype.matchall": "^4.0.7",
"stringify": "^5.2.0",
"style-loader": "^3.3.1",
"ts-loader": "^9.4.1",
"ts-node": "^10.7.0",
"typescript": "^4.8.3",
"underscore": "^1.13.6",
"url-loader": "^4.1.1",
"webpack": "^5.74.0",
"webpack-cli": "^4.10.0",
"webpack-dev-server": "^4.11.1",
"webpack-merge": "^5.8.0",
"websocket-stream": "^5.5.2",
"ws": "^8.9.0"
},
"dependencies": {
"@webpack-cli/configtest": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-1.2.0.tgz",
"integrity": "sha512-4FB8Tj6xyVkyqjj1OaTqCjXYULB9FMkqQ8yGrZjRDrYh0nOE+7Lhs45WioWQQMV+ceFlE368Ukhe6xdvJM9Egg==",
"dev": true,
"requires": {}
},
"@webpack-cli/info": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-1.5.0.tgz",
"integrity": "sha512-e8tSXZpw2hPl2uMJY6fsMswaok5FdlGNRTktvFk2sD8RjH0hE2+XistawJx1vmKteh4NmGmNUrp+Tb2w+udPcQ==",
"dev": true,
"requires": {
"envinfo": "^7.7.3"
}
},
"@webpack-cli/serve": {
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-1.7.0.tgz",
"integrity": "sha512-oxnCNGj88fL+xzV+dacXs44HcDwf1ovs3AuEzvP7mqXw7fQntqIhQ1BRmynh4qEKQSSSRSWVyXRjmTbZIX9V2Q==",
"dev": true,
"requires": {}
},
"commander": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
"integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
"dev": true
},
"rechoir": {
"version": "0.7.1",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.7.1.tgz",
"integrity": "sha512-/njmZ8s1wVeR6pjTZ+0nCnv8SpZNRMT2D1RLOJQESlYFDBvwpTA4KWJpZ+sBJ4+vhjILRcK7JIFdGCdxEAAitg==",
"dev": true,
"requires": {
"resolve": "^1.9.0"
}
},
"webpack-cli": {
"version": "4.10.0",
"resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-4.10.0.tgz",
"integrity": "sha512-NLhDfH/h4O6UOy+0LSso42xvYypClINuMNBVVzX4vX98TmTaTUxwRbXdhucbFMd2qLaCTcLq/PdYrvi8onw90w==",
"dev": true,
"requires": {
"@discoveryjs/json-ext": "^0.5.0",
"@webpack-cli/configtest": "^1.2.0",
"@webpack-cli/info": "^1.5.0",
"@webpack-cli/serve": "^1.7.0",
"colorette": "^2.0.14",
"commander": "^7.0.0",
"cross-spawn": "^7.0.3",
"fastest-levenshtein": "^1.0.12",
"import-local": "^3.0.2",
"interpret": "^2.2.0",
"rechoir": "^0.7.0",
"webpack-merge": "^5.7.3"
}
}
}
},
"nopt": {
"version": "1.0.10",
"requires": {
@@ -86403,6 +86427,11 @@
"underscore": {
"version": "1.13.6"
},
"undici-types": {
"version": "5.26.5",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
"integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="
},
"unfetch": {
"version": "4.2.0",
"dev": true

View File

@@ -12,7 +12,7 @@
"graph": "npx nx graph",
"ci:prepare:editor": "ts-node ./scripts/ci-editor-prepare.ts",
"ci:build:viewer": "lerna exec --scope @noodl/noodl-viewer-react -- npm run build",
"ci:build:editor": "lerna exec --scope noodl-editor -- npm run ci:build",
"ci:build:editor": "lerna exec --scope fluxscape-editor -- npm run ci:build",
"build:editor": "ts-node ./scripts/build-editor.ts",
"build:editor:_viewer": "ts-node ./scripts/noodl-editor/build-viewer.ts",
"build:editor:_editor": "ts-node ./scripts/noodl-editor/build-editor.ts",
@@ -20,7 +20,7 @@
"build:cloud-runtime": "lerna run build --scope @noodl/cloud-runtime --stream && lerna run build:pack --scope @noodl/cloud-runtime --stream",
"start:storybook": "lerna exec --scope @noodl/noodl-core-ui -- npm run start",
"start:viewer": "lerna run start --scope @noodl/noodl-viewer-react --stream",
"start:editor": "lerna run start --scope noodl-editor --stream",
"start:editor": "lerna run start --scope fluxscape-editor --stream",
"dev": "ts-node ./scripts/start.ts",
"start": "ts-node ./scripts/start.ts -- --build-viewer",
"test:editor": "ts-node ./scripts/test-editor.ts",
@@ -47,4 +47,4 @@
"npm": ">=6.0.0",
"node": ">=16.0.0 <=18"
}
}
}

View File

@@ -1,123 +1,128 @@
.Root {
border: none;
padding: 0;
background: transparent;
box-sizing: border-box;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
&.has-backdrop {
background-color: var(--theme-color-bg-1-transparent);
}
&.is-locking-scroll {
background-color: transparent;
}
&:not(.has-backdrop):not(.is-locking-scroll) {
pointer-events: none;
}
}
.VisibleDialog {
filter: drop-shadow(0 4px 15px var(--theme-color-bg-1-transparent-2));
box-shadow: 0 0 10px -5px var(--theme-color-bg-1-transparent-2);
position: absolute;
width: var(--width);
pointer-events: all;
.Root.is-centered & {
top: 50%;
left: 50%;
animation: enter-centered var(--speed-quick) var(--easing-base) both;
}
.Root:not(.is-centered) &.is-visible {
&.is-variant-default {
animation: enter var(--speed-quick) var(--easing-base) both;
}
&.is-variant-select {
transform: translate(var(--offsetX), var(--offsetY));
}
}
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--background);
border-radius: 2px;
overflow: hidden;
}
}
.Arrow {
position: absolute;
width: 0;
height: 0;
top: var(--arrow-top);
left: var(--arrow-left);
pointer-events: none;
&::after {
content: '';
display: block;
width: 11px;
height: 11px;
transform: translate(-50%, -50%) rotate(45deg);
background: var(--background);
}
&.is-contrast::after {
background: var(--backgroundContrast);
}
}
.Title {
background-color: var(--backgroundContrast);
padding: 12px;
}
.MeasuringContainer {
pointer-events: none;
height: 0;
overflow: visible;
opacity: 0;
}
.ChildContainer {
position: relative;
z-index: 1;
}
@keyframes enter {
from {
opacity: 0;
transform: translate(
calc(var(--animationStartOffsetX) + var(--offsetX)),
calc(var(--animationStartOffsetY) + var(--offsetY))
);
}
to {
opacity: 1;
transform: translate(var(--offsetX), var(--offsetY));
}
}
@keyframes enter-centered {
from {
opacity: 0;
transform: translate(-50%, calc(-50% + 16px));
}
to {
opacity: 1;
transform: translate(-50%, -50%);
}
}
.Root {
border: none;
padding: 0;
background: transparent;
box-sizing: border-box;
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
&.is-hidden {
visibility: hidden;
pointer-events: none;
}
&.has-backdrop {
background-color: var(--theme-color-bg-1-transparent);
}
&.is-locking-scroll {
background-color: transparent;
}
&:not(.has-backdrop):not(.is-locking-scroll) {
pointer-events: none;
}
}
.VisibleDialog {
filter: drop-shadow(0 4px 15px var(--theme-color-bg-1-transparent-2));
box-shadow: 0 0 10px -5px var(--theme-color-bg-1-transparent-2);
position: absolute;
width: var(--width);
pointer-events: all;
.Root.is-centered & {
top: 50%;
left: 50%;
animation: enter-centered var(--speed-quick) var(--easing-base) both;
}
.Root:not(.is-centered) &.is-visible {
&.is-variant-default {
animation: enter var(--speed-quick) var(--easing-base) both;
}
&.is-variant-select {
transform: translate(var(--offsetX), var(--offsetY));
}
}
&::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: var(--background);
border-radius: 2px;
overflow: hidden;
}
}
.Arrow {
position: absolute;
width: 0;
height: 0;
top: var(--arrow-top);
left: var(--arrow-left);
pointer-events: none;
&::after {
content: '';
display: block;
width: 11px;
height: 11px;
transform: translate(-50%, -50%) rotate(45deg);
background: var(--background);
}
&.is-contrast::after {
background: var(--backgroundContrast);
}
}
.Title {
background-color: var(--backgroundContrast);
padding: 12px;
}
.MeasuringContainer {
pointer-events: none;
height: 0;
overflow: visible;
opacity: 0;
}
.ChildContainer {
position: relative;
z-index: 1;
}
@keyframes enter {
from {
opacity: 0;
transform: translate(
calc(var(--animationStartOffsetX) + var(--offsetX)),
calc(var(--animationStartOffsetY) + var(--offsetY))
);
}
to {
opacity: 1;
transform: translate(var(--offsetX), var(--offsetY));
}
}
@keyframes enter-centered {
from {
opacity: 0;
transform: translate(-50%, calc(-50% + 16px));
}
to {
opacity: 1;
transform: translate(-50%, -50%);
}
}

View File

@@ -42,6 +42,7 @@ export interface BaseDialogProps extends UnsafeStyleProps {
isVisible?: boolean;
hasBackdrop?: boolean;
hasArrow?: boolean;
alwaysMounted?: boolean;
children?: Slot;
@@ -69,6 +70,7 @@ export function CoreBaseDialog({
isVisible,
hasBackdrop,
hasArrow,
alwaysMounted,
children,
@@ -261,7 +263,7 @@ export function CoreBaseDialog({
}
}, [background]);
if (!isVisible) return null;
if (!isVisible && !alwaysMounted) return null;
return (
<div
@@ -270,6 +272,7 @@ export function CoreBaseDialog({
hasBackdrop && css['has-backdrop'],
isLockingScroll && css['is-locking-scroll'],
typeof triggerRef === 'undefined' && css['is-centered'],
!isVisible && css['is-hidden'],
css[variant]
)}
onClick={onClose}

View File

@@ -1,10 +1,10 @@
{
"name": "noodl-editor",
"name": "fluxscape-editor",
"productName": "Fluxscape",
"description": "Node-Based App Builder for Scalability & Rapid Development, a fork of Noodl",
"author": "Fluxscape <contact@fluxscape.io>",
"homepage": "https://fluxscape.io",
"version": "1.0.0",
"version": "1.1.0",
"main": "src/main/main.js",
"scripts": {
"build": "npx ts-node -P ./tsconfig.build.json ./scripts/build.ts",
@@ -58,7 +58,7 @@
]
},
"dependencies": {
"@electron/remote": "^2.1.1",
"@electron/remote": "^2.1.2",
"@jaames/iro": "^5.5.2",
"@microsoft/fetch-event-source": "^2.0.1",
"@noodl/git": "file:../noodl-git",
@@ -114,7 +114,7 @@
"babel-loader": "^8.2.4",
"concurrently": "^7.4.0",
"css-loader": "^6.7.1",
"electron": "28.1.4",
"electron": "31.3.1",
"electron-builder": "^24.9.1",
"file-loader": "^6.2.0",
"html-loader": "^3.1.0",
@@ -141,4 +141,4 @@
"optionalDependencies": {
"dmg-license": "^1.0.11"
}
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.3 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM19 18H6c-2.21 0-4-1.79-4-4 0-2.05 1.53-3.76 3.56-3.97l1.07-.11.5-.95C8.08 7.14 9.94 6 12 6c2.62 0 4.88 1.86 5.39 4.43l.3 1.5 1.53.11c1.56.1 2.78 1.41 2.78 2.96 0 1.65-1.35 3-3 3zm-9-3.82l-2.09-2.09L6.5 13.5 10 17l6.01-6.01-1.41-1.41z"/></svg>

Before

Width:  |  Height:  |  Size: 526 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 540 KiB

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>

Before

Width:  |  Height:  |  Size: 175 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none"><path d="M0 0h24v24H0V0z"/><path opacity=".87" d="M0 0h24v24H0V0z"/></g><path d="M21 3.01H3c-1.1 0-2 .9-2 2V9h2V4.99h18v14.03H3V15H1v4.01c0 1.1.9 1.98 2 1.98h18c1.1 0 2-.88 2-1.98v-14c0-1.11-.9-2-2-2zM11 16l4-4-4-4v3H1v2h10v3zM23 3.01H1V9h2V4.99h18v14.03H3V15H1v5.99h22V3.01zM11 16l4-4-4-4v3H1v2h10v3z"/></svg>

Before

Width:  |  Height:  |  Size: 408 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M4 6H2v16h16v-2H4V6zm18-4H6v16h16V2zm-3 9H9V9h10v2zm-4 4H9v-2h6v2zm4-8H9V5h10v2z"/></svg>

Before

Width:  |  Height:  |  Size: 220 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><g fill="none"><path d="M0 0h24v24H0V0z"/><path opacity=".87" d="M0 0h24v24H0V0z"/></g><path d="M3 13h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7zm-4 6h2v-2H3v2zm0 4h2v-2H3v2zm0-8h2V7H3v2zm4 4h14v-2H7v2zm0 4h14v-2H7v2zM7 7v2h14V7H7z"/></svg>

Before

Width:  |  Height:  |  Size: 360 B

View File

@@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path fill="none" d="M0 0h24v24H0V0z"/><path d="M19.44 12.99l-.01.02c.04-.33.08-.67.08-1.01 0-.34-.03-.66-.07-.99l.01.02 2.44-1.92-2.43-4.22-2.87 1.16.01.01c-.52-.4-1.09-.74-1.71-1h.01L14.44 2H9.57l-.44 3.07h.01c-.62.26-1.19.6-1.71 1l.01-.01-2.88-1.17-2.44 4.22 2.44 1.92.01-.02c-.04.33-.07.65-.07.99 0 .34.03.68.08 1.01l-.01-.02-2.1 1.65-.33.26 2.43 4.2 2.88-1.15-.02-.04c.53.41 1.1.75 1.73 1.01h-.03L9.58 22h4.85s.03-.18.06-.42l.38-2.65h-.01c.62-.26 1.2-.6 1.73-1.01l-.02.04 2.88 1.15 2.43-4.2s-.14-.12-.33-.26l-2.11-1.66zM12 15.5c-1.93 0-3.5-1.57-3.5-3.5s1.57-3.5 3.5-3.5 3.5 1.57 3.5 3.5-1.57 3.5-3.5 3.5z"/></svg>

Before

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 522 KiB

View File

@@ -16,6 +16,7 @@ import { projectFromDirectory, unzipIntoDirectory } from '../models/projectmodel
import FileSystem from './filesystem';
import { tracker } from './tracker';
import { guid } from './utils';
import { getTopLevelWorkingDirectory } from '@noodl/git/src/core/open';
export interface ProjectItem {
id: string;
@@ -267,12 +268,15 @@ export class LocalProjectsModel extends Model {
});
}
isGitProject(project: ProjectModel): boolean {
// TODO: check if there's is git in any parent folder too
// Check if the git folder exists.
const gitPath = filesystem.join(project._retainedProjectDirectory, '.git');
return filesystem.exists(gitPath);
/**
* Check if this project is in a git repository.
*
* @param project
* @returns
*/
async isGitProject(project: ProjectModel): Promise<boolean> {
const gitPath = await getTopLevelWorkingDirectory(project._retainedProjectDirectory);
return gitPath !== null;
}
setCurrentGlobalGitAuth(projectId: string) {

View File

@@ -6,7 +6,7 @@ import { ITemplateProvider, ProgressCallback, TemplateItem, TemplateListFilter }
*/
export class NoodlDocsTemplateProvider implements ITemplateProvider {
get name(): string {
return 'https://docs.noodl.net';
return this.getDocsEndpoint() || 'https://docs.fluxscape.io';
}
constructor(private readonly getDocsEndpoint: () => string) {}

View File

@@ -11,6 +11,14 @@ export interface ArrayDiff<T> {
changed: T[];
unchanged: T[];
}
export function createEmptyArrayDiff<T>(): ArrayDiff<T> {
return {
deleted: [],
created: [],
changed: [],
unchanged: [],
}
}
export interface ProjectDiffItem {
graph: TSFixme;

View File

@@ -13,6 +13,7 @@ export default class SchemaHandler {
public dbCollections: TSFixme[];
public systemCollections: TSFixme[];
public configSchema: TSFixme;
public parseServerVersion: string;
constructor() {
EventDispatcher.instance.on(
@@ -119,6 +120,20 @@ export default class SchemaHandler {
console.log(e);
}
});
// Get Parse Server Version & Supported features
fetch(environment.url + '/serverInfo', {
method: 'POST',
body: JSON.stringify({
"_method": "GET",
"_ApplicationId": environment.appId,
"_MasterKey": environment.masterKey,
})
})
.then((response) => response.json())
.then((json) => {
this.parseServerVersion = json.parseServerVersion;
});
});
});
}
@@ -129,10 +144,20 @@ export default class SchemaHandler {
ProjectModel.instance.setMetaData('dbCollections', this.dbCollections);
ProjectModel.instance.setMetaData('systemCollections', this.systemCollections);
ProjectModel.instance.setMetaData('dbConfigSchema', this.configSchema);
const versionNumbers = this.parseServerVersion?.split(".")
if (versionNumbers && versionNumbers.length > 0) {
// Let's only save the major version number,
// since this will be used to determine which verison of the API to use.
ProjectModel.instance.setMetaData('dbVersionMajor', versionNumbers[0]);
} else {
ProjectModel.instance.setMetaData('dbVersionMajor', undefined);
}
} else {
ProjectModel.instance.setMetaData('dbCollections', undefined);
ProjectModel.instance.setMetaData('systemCollections', undefined);
ProjectModel.instance.setMetaData('dbConfigSchema', undefined);
ProjectModel.instance.setMetaData('dbVersionMajor', undefined);
}
}
}

View File

@@ -1,5 +1,6 @@
import { usePluginContext } from '@noodl-contexts/PluginContext';
import React, { RefObject, useEffect, useRef } from 'react';
import React, { RefObject } from 'react';
import { ProjectModel } from '@noodl-models/projectmodel';
import { ActivityIndicator } from '@noodl-core-ui/components/common/ActivityIndicator';
import { BaseDialog } from '@noodl-core-ui/components/layout/BaseDialog';
@@ -54,6 +55,7 @@ export function DeployPopup(props: DeployPopupProps) {
onClose={props.onClose}
hasArrow
isLockingScroll
alwaysMounted
>
<DeployPopupChild />
</BaseDialog>
@@ -62,6 +64,19 @@ export function DeployPopup(props: DeployPopupProps) {
}
function FluxscapeDeployTab() {
// Preview URL: 'http://192.168.0.33:8574/'
return <iframe src="https://portal.fluxscape.io" style={{ width: "100%", height: "50vh", borderStyle: "none" }} />;
const params = {};
const projectId = ProjectModel.instance.id;
if (projectId) {
params['projectId'] = projectId;
}
const urlParams = new URLSearchParams(params);
return (
<iframe
src={`https://portal.fluxscape.io/?${urlParams.toString()}`}
style={{ width: '100%', height: '50vh', borderStyle: 'none' }}
/>
);
}

View File

@@ -199,6 +199,7 @@ function BaseVersionControlPanel() {
export function VersionControlPanel() {
const [git, setGit] = useState<Git>(null);
const [state, setState] = useState<'loading' | 'loaded' | 'not-git'>('loading');
async function createGit() {
const gitClient = new Git(mergeProject);
@@ -206,12 +207,18 @@ export function VersionControlPanel() {
setGit(gitClient);
}
const isGitProject = git === null ? LocalProjectsModel.instance.isGitProject(ProjectModel.instance) : true;
useEffect(() => {
if (isGitProject) {
createGit();
}
}, [isGitProject]);
LocalProjectsModel.instance
.isGitProject(ProjectModel.instance)
.then(async (isGitProject) => {
if (isGitProject) {
await createGit();
setState('loaded');
} else {
setState('not-git');
}
});
}, []);
async function setupGit() {
const gitClient = new Git(mergeProject);
@@ -220,7 +227,7 @@ export function VersionControlPanel() {
setGit(gitClient);
}
if (git === null && !isGitProject) {
if (git === null && state === 'not-git') {
return (
<BasePanel isFill title="Version Control">
<Box hasXSpacing hasYSpacing>

View File

@@ -6,11 +6,13 @@ import { Commit } from '@noodl/git/src/core/models/snapshot';
import { FileChange } from '@noodl/git/src/core/models/status';
import { revRange } from '@noodl/git/src/core/rev-list';
import { ProjectModel } from '@noodl-models/projectmodel';
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
import { mergeProject } from '@noodl-utils/projectmerger';
import { ProjectDiff, diffProject } from '@noodl-utils/projectmerger.diff';
import { useVersionControlContext } from '../context';
import { getProjectFilePath } from '../context/DiffUtils';
import { DiffList } from './DiffList';
//Kind:
@@ -124,7 +126,10 @@ async function getMergeDiff(repositoryPath: string, commit: Commit, refToDiffTo:
}
async function getProjectFile(commit: Commit) {
const projectContent = JSON.parse(await commit.getFileAsString('project.json'));
const projectFilePath = getProjectFilePath(commit.repositoryDir, ProjectModel.instance._retainedProjectDirectory);
const projectContentRaw = await commit.getFileAsString(projectFilePath);
const projectContent = JSON.parse(projectContentRaw);
applyPatches(projectContent);
return projectContent;
}

View File

@@ -61,7 +61,7 @@ export function DiffList({ diff, fileChanges, componentDiffTitle, actions, commi
const [imageDiff, setImageDiff] = useState<IImageDiff>(null);
const components = diff?.components ? getChangedComponents(diff.components) : [];
const files = (fileChanges || [])?.filter((f) => f.path !== 'project.json') || [];
const files = (fileChanges || [])?.filter((f) => !f.path.endsWith('project.json')) || [];
const settings = diff?.settings ? getChangedObjectProperties(diff.settings) : [];
const colorStyles = diff?.styles.colors ? getChangedObjectProperties(diff.styles.colors) : [];
const textStyles = diff?.styles.text ? getChangedObjectProperties(diff.styles.text) : [];

View File

@@ -1,21 +1,24 @@
import path from 'path';
import { getCommit } from '@noodl/git/src/core/logs';
import { FileStatusKind } from '@noodl/git/src/core/models/status';
import { FeedbackType } from '@noodl-constants/FeedbackType';
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
import {
ProjectDiff,
ProjectDiffItem,
ProjectBasicDiffItem,
ArrayDiff,
diffProject
diffProject,
createEmptyArrayDiff
} from '@noodl-utils/projectmerger.diff';
import { IconName } from '@noodl-core-ui/components/common/Icon';
import { ListItemProps } from '@noodl-core-ui/components/layout/ListItem';
import { ProjectModel } from '../../../../models/projectmodel';
import { applyPatches } from '@noodl-models/ProjectPatches/applypatches';
import { FileStatusKind } from '@noodl/git/src/core/models/status';
import { IconName } from '@noodl-core-ui/components/common/Icon';
import { ListItemProps } from '@noodl-core-ui/components/layout/ListItem';
import { FeedbackType } from '@noodl-constants/FeedbackType';
import { getCommit } from '@noodl/git/src/core/logs';
export interface ProjectLocalDiff extends ProjectDiff{
export interface ProjectLocalDiff extends ProjectDiff {
baseProject: TSFixme; //Project model as an object from raw json
commitShaDiffedTo: string;
}
@@ -84,17 +87,49 @@ export function getFileStatusIconProps(status: FileStatusKind): Partial<ListItem
}
}
export async function doLocalDiff(repositoryPath: string, headCommitId: string): Promise<ProjectLocalDiff> {
const baseCommit = await getCommit(repositoryPath, headCommitId);
const baseProjectJson = await baseCommit.getFileAsString('project.json');
const baseProject = JSON.parse(baseProjectJson);
applyPatches(baseProject);
const diff = diffProject(baseProject, ProjectModel.instance.toJSON());
return {
...diff,
baseProject,
commitShaDiffedTo: headCommitId
};
export function getProjectFilePath(repositoryPath: string, projectPath: string) {
const relativePath = path.relative(repositoryPath, projectPath);
const projectFilePath = path.join(relativePath, 'project.json').replaceAll('\\', '/');
return projectFilePath;
}
export async function doLocalDiff(
repositoryPath: string,
projectPath: string,
headCommitId: string
): Promise<ProjectLocalDiff> {
const projectFilePath = getProjectFilePath(repositoryPath, projectPath);
try {
const baseCommit = await getCommit(projectPath, headCommitId);
const baseProjectJson = await baseCommit.getFileAsString(projectFilePath);
const baseProject = JSON.parse(baseProjectJson);
applyPatches(baseProject);
const diff = diffProject(baseProject, ProjectModel.instance.toJSON());
return {
...diff,
baseProject,
commitShaDiffedTo: headCommitId
};
} catch (error) {
if (error.toString().includes('exists on disk, but not in')) {
console.warn('project.json does not exist in this commit.');
}
// Return empty state
return {
baseProject: {},
commitShaDiffedTo: headCommitId,
components: createEmptyArrayDiff(),
variants: createEmptyArrayDiff(),
settings: createEmptyArrayDiff(),
styles: {
colors: createEmptyArrayDiff(),
text: createEmptyArrayDiff()
},
cloudservices: createEmptyArrayDiff()
};
}
}

View File

@@ -8,6 +8,7 @@ import { Slot } from '@noodl-core-ui/types/global';
import { doLocalDiff, ProjectLocalDiff } from './DiffUtils';
import { useVersionControlFetch } from './fetch.context';
import { BranchStatus, IVersionControlContext } from './types';
import { ProjectModel } from '@noodl-models/projectmodel';
const VersionControlContext = createContext<IVersionControlContext>({
git: null,
@@ -57,7 +58,8 @@ export function VersionControlProvider({ git, children }: { git: Git; children:
(async () => {
const currentCommitSha = await git.getHeadCommitId();
if (currentCommitSha) {
const diff = await doLocalDiff(git.repositoryPath, currentCommitSha);
const projectPath = ProjectModel.instance._retainedProjectDirectory;
const diff = await doLocalDiff(git.repositoryPath, projectPath, currentCommitSha);
setLocalDiff(diff);
}
})();

View File

@@ -5,6 +5,8 @@ import { useSidePanelKeyboardCommands } from '@noodl-hooks/useKeyboardCommands';
import classNames from 'classnames';
import React, { useEffect, useRef, useState } from 'react';
import { ComponentModel } from '@noodl-models/componentmodel';
import { NodeGraphNode } from '@noodl-models/nodegraphmodel';
import { KeyCode, KeyMod } from '@noodl-utils/keyboard/KeyCode';
import { performSearch } from '@noodl-utils/universal-search';
@@ -54,7 +56,7 @@ export function SearchPanel() {
}
}, [debouncedSearchTerm]);
function onSearchItemClicked(searchResult) {
function onSearchItemClicked(searchResult: SearchResultItem) {
if (searchResult.type === 'Component') {
NodeGraphContextTmp.switchToComponent(searchResult.componentTarget, {
breadcrumbs: false,
@@ -85,29 +87,7 @@ export function SearchPanel() {
<div className={css.SearchResults}>
{searchResults.map((component) => (
<Section
title={`${component.componentName} (${component.results.length} result${
component.results.length > 1 ? 's' : ''
})`}
key={component.componentId}
variant={SectionVariant.Panel}
>
{component.results.map((result, index) => (
<div
className={classNames(
css.SearchResultItem
// lastActiveComponentId === result.componentTarget.id && css['is-active']
)}
key={index}
onClick={() => onSearchItemClicked(result)}
>
<Label variant={TextType.Proud}>
{result.userLabel ? result.type + ' - ' + result.userLabel : result.type}
</Label>
<Text>{result.label}</Text>
</div>
))}
</Section>
<SearchItem key={component.componentId} component={component} onSearchItemClicked={onSearchItemClicked} />
))}
{searchResults.length === 0 && debouncedSearchTerm.length > 0 && (
<Container hasXSpacing hasYSpacing>
@@ -118,3 +98,51 @@ export function SearchPanel() {
</BasePanel>
);
}
type SearchResultItem = {
componentTarget: ComponentModel;
label: string;
nodeTarget: NodeGraphNode;
type: string;
userLabel: string;
};
type SearchItemProps = {
component: {
componentName: string;
componentId: string;
results: SearchResultItem[];
};
onSearchItemClicked: (item: SearchResultItem) => void;
};
function SearchItem({ component, onSearchItemClicked }: SearchItemProps) {
const resultCountText = `${component.results.length} result${component.results.length > 1 ? 's' : ''}`;
let titleText = `${component.componentName} (${resultCountText})`;
// We expect there to always be at least one result, to get the full component name.
const isInCloudFunctions = component.results[0].componentTarget.name.startsWith('/#__cloud__/');
if (isInCloudFunctions) {
titleText += ' (Cloud Function)';
}
return (
<Section title={titleText} variant={SectionVariant.Panel}>
{component.results.map((result, index) => (
<div
className={classNames(
css.SearchResultItem
// lastActiveComponentId === result.componentTarget.id && css['is-active']
)}
key={index}
onClick={() => onSearchItemClicked(result)}
>
<Label variant={TextType.Proud}>
{result.userLabel ? result.type + ' - ' + result.userLabel : result.type}
</Label>
<Text>{result.label}</Text>
</div>
))}
</Section>
);
}

View File

@@ -22,7 +22,11 @@ export async function open(basePath: string): Promise<string> {
// console.log("VCS error when opening project: " + e);
// }
return basePath;
// Find the relative git repository path
const repositoryPath = await getTopLevelWorkingDirectory(basePath);
return repositoryPath;
}
/**

View File

@@ -32,12 +32,15 @@ class CloudStore {
_initCloudServices() {
_collections = undefined; // clear collection cache, so it's refetched
const cloudServices = NoodlRuntime.instance.getMetaData('cloudservices');
const cloudServices = NoodlRuntime.instance.getMetaData('cloudservices');
if (cloudServices) {
this.appId = cloudServices.appId;
this.endpoint = cloudServices.endpoint;
}
const dbVersionMajor = NoodlRuntime.instance.getMetaData('dbVersionMajor');
this.dbVersionMajor = dbVersionMajor;
}
on() {
@@ -168,13 +171,10 @@ class CloudStore {
return;
}
if (options.where) args.push('match=' + encodeURIComponent(JSON.stringify(options.where)));
if (options.limit) args.push('limit=' + options.limit);
if (options.skip) args.push('skip=' + options.skip);
const grouping = {
objectId: null
};
const grouping = {};
Object.keys(options.group).forEach((k) => {
const _g = {};
@@ -188,7 +188,20 @@ class CloudStore {
grouping[k] = _g;
});
args.push('group=' + JSON.stringify(grouping));
// I don't know which version the API was changed, lets just say above 4 for now.
if (this.dbVersionMajor && this.dbVersionMajor > 4) {
grouping._id = null;
if (options.where) args.push('$match=' + encodeURIComponent(JSON.stringify(options.where)));
args.push('$group=' + JSON.stringify(grouping));
} else {
grouping.objectId = null;
if (options.where) args.push('match=' + encodeURIComponent(JSON.stringify(options.where)));
args.push('group=' + JSON.stringify(grouping));
}
this._makeRequest('/aggregate/' + options.collection + (args.length > 0 ? '?' + args.join('&') : ''), {
success: function (response) {

View File

@@ -12,6 +12,7 @@ function createRecordsAPI(modelScope) {
return {
async query(className, query, options) {
if (typeof className === "undefined") throw new Error("'className' is undefined");
return new Promise((resolve, reject) => {
cloudstore().query({
collection: className,
@@ -39,6 +40,7 @@ function createRecordsAPI(modelScope) {
},
async count(className, query) {
if (typeof className === "undefined") throw new Error("'className' is undefined");
return new Promise((resolve, reject) => {
cloudstore().count({
collection: className,
@@ -60,6 +62,7 @@ function createRecordsAPI(modelScope) {
},
async distinct(className, property, query) {
if (typeof className === "undefined") throw new Error("'className' is undefined");
return new Promise((resolve, reject) => {
cloudstore().distinct({
collection: className,
@@ -82,6 +85,7 @@ function createRecordsAPI(modelScope) {
},
async aggregate(className, group, query) {
if (typeof className === "undefined") throw new Error("'className' is undefined");
return new Promise((resolve, reject) => {
cloudstore().aggregate({
collection: className,
@@ -104,6 +108,7 @@ function createRecordsAPI(modelScope) {
},
async fetch(objectOrId, options) {
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
@@ -126,6 +131,7 @@ function createRecordsAPI(modelScope) {
},
async increment(objectOrId, properties, options) {
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
@@ -149,6 +155,7 @@ function createRecordsAPI(modelScope) {
},
async save(objectOrId, properties, options) {
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
@@ -179,6 +186,7 @@ function createRecordsAPI(modelScope) {
},
async create(className, properties, options) {
if (typeof className === "undefined") throw new Error("'className' is undefined");
return new Promise((resolve, reject) => {
cloudstore().create({
collection: className,
@@ -197,6 +205,7 @@ function createRecordsAPI(modelScope) {
},
async delete(objectOrId, options) {
if (typeof objectOrId === 'undefined') return Promise.reject(new Error("'objectOrId' is undefined."));
if (typeof objectOrId !== 'string') objectOrId = objectOrId.getId();
const className = (options ? options.className : undefined) || (modelScope || Model).get(objectOrId)._class;
@@ -265,7 +274,7 @@ function createRecordsAPI(modelScope) {
resolve();
},
error: (err) => {
reject(Error(rr || 'Failed to add relation.'));
reject(Error(err || 'Failed to add relation.'));
}
});
});

View File

@@ -2,6 +2,8 @@ const StringFormatDefinition = {
name: 'String Format',
docs: 'https://docs.noodl.net/nodes/string-manipulation/string-format',
category: 'String Manipulation',
usePortAsLabel: 'format',
portLabelTruncationMode: 'length',
initialize() {
const internal = this._internal;
internal.format = '';

View File

@@ -7,6 +7,8 @@ import { Noodl, Slot } from '../../../types';
export interface ButtonProps extends Noodl.ReactProps {
enabled: boolean;
buttonType: 'button' | 'submit';
attrs: React.Attributes;
textStyle: Noodl.TextStyle;
@@ -96,6 +98,7 @@ export function Button(props: ButtonProps) {
return (
<button
{...props.attrs}
className={className}
disabled={!props.enabled}
{...Utils.controlEvents(props)}

View File

@@ -9,6 +9,8 @@ export interface CheckboxProps extends Noodl.ReactProps {
enabled: boolean;
checked: boolean;
attrs: React.Attributes;
useLabel: boolean;
label: string;
labelSpacing: string;
@@ -47,6 +49,7 @@ export function Checkbox(props: CheckboxProps) {
Layout.align(style, props);
const inputProps = {
...props.attrs,
id: props.id,
className: [props.className, 'ndl-controls-checkbox-2'].join(' '),
disabled: !props.enabled,

View File

@@ -10,6 +10,8 @@ export interface RadioButtonProps extends Noodl.ReactProps {
enabled: boolean;
value: string;
attrs: React.Attributes;
useLabel: boolean;
label: string;
labelSpacing: string;
@@ -47,6 +49,7 @@ export function RadioButton(props: RadioButtonProps) {
props.checkedChanged && props.checkedChanged(radioButtonGroup ? radioButtonGroup.selected === props.value : false);
const inputProps = {
...props.attrs,
id: props.id,
disabled: !props.enabled,
className: [props.className, 'ndl-controls-radio-2'].join(' '),

View File

@@ -12,6 +12,8 @@ export interface SelectProps extends Noodl.ReactProps {
textStyle: Noodl.TextStyle;
items: TSFixme;
attrs: React.Attributes;
placeholder: string;
placeholderOpacity: string;
@@ -81,6 +83,7 @@ export function Select(props: SelectProps) {
}
const inputProps = {
...props.attrs,
id: props.id,
className: props.className,
style: {

View File

@@ -9,6 +9,8 @@ export interface SliderProps extends Noodl.ReactProps {
id: string;
enabled: boolean;
attrs: React.Attributes;
value: number;
min: number;
max: number;
@@ -103,6 +105,7 @@ export function Slider(props: SliderProps) {
const className = `ndl-controls-range2 ${instanceClassId} ${props.className ? props.className : ''} `;
const inputProps: React.InputHTMLAttributes<HTMLInputElement> = {
...props.attrs,
id: props.id,
style: {
width: '100%',

View File

@@ -17,6 +17,8 @@ export interface TextInputProps extends Noodl.ReactProps {
type: 'text' | 'textArea' | 'email' | 'number' | 'password' | 'url';
textStyle: Noodl.TextStyle;
attrs: React.Attributes;
enabled: boolean;
placeholder: string;
@@ -149,6 +151,7 @@ export class TextInput extends React.Component<TextInputProps, State> {
inputStyles.color = props.noodlNode.context.styles.resolveColor(inputStyles.color);
const inputProps = {
...props.attrs,
id: props.id,
value: this.state.value,
...Utils.controlEvents(props),

View File

@@ -7,6 +7,8 @@ export interface ColumnsProps extends Noodl.ReactProps {
marginX: string;
marginY: string;
attrs: React.Attributes;
justifyContent: 'flex-start' | 'flex-end' | 'center';
direction: 'row' | 'column';
minWidth: string;
@@ -115,7 +117,10 @@ export function Columns(props: ColumnsProps) {
// ForEachCompoent breaks the layout but is needed to send onMount/onUnmount
if (!Array.isArray(props.children)) {
children = [props.children];
// @ts-expect-error props.children.type is any
if (props.children.type !== ForEachComponent) {
children = [props.children]
}
} else {
children = props.children.filter((child) => child.type !== ForEachComponent);
forEachComponent = props.children.find((child) => child.type === ForEachComponent);
@@ -123,9 +128,11 @@ export function Columns(props: ColumnsProps) {
return (
<div
{...props.attrs}
className={['columns-container', props.className].join(' ')}
ref={containerRef}
style={{
visibility: containerWidth === null ? "hidden" : "visible",
marginTop: parseFloat(props.marginY) * -1,
marginLeft: parseFloat(props.marginX) * -1,
display: 'flex',

View File

@@ -19,6 +19,8 @@ BScroll.use(Slide);
export interface GroupProps extends Noodl.ReactProps {
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
attrs: React.Attributes;
scrollSnapEnabled: boolean;
showScrollbar: boolean;
scrollEnabled: boolean;
@@ -271,6 +273,7 @@ export class Group extends React.Component<GroupProps> {
<Component
// @ts-expect-error Lets hope that the type passed here is always static!
className={props.className}
{...props.attrs}
{...props.dom}
{...PointerListeners(props)}
style={style}

View File

@@ -10,6 +10,7 @@ export interface ImageProps extends Noodl.ReactProps {
src: string;
onLoad?: () => void;
};
attrs: React.Attributes;
}
export function Image(props: ImageProps) {
@@ -30,5 +31,5 @@ export function Image(props: ImageProps) {
}
}
return <img className={props.className} {...props.dom} {...PointerListeners(props)} style={style} />;
return <img {...props.attrs} className={props.className} {...props.dom} {...PointerListeners(props)} style={style} />;
}

View File

@@ -7,6 +7,8 @@ import { Noodl } from '../../../types';
export interface TextProps extends Noodl.ReactProps {
as?: keyof JSX.IntrinsicElements | React.ComponentType<unknown>;
attrs: React.Attributes;
textStyle: Noodl.TextStyle;
text: string;
@@ -48,6 +50,7 @@ export function Text(props: TextProps) {
return (
<Component
className={['ndl-visual-text', props.className].join(' ')}
{...props.attrs}
{...props.dom}
{...PointerListeners(props)}
style={style}

View File

@@ -30,11 +30,24 @@ const ButtonNode = {
]
},
initialize() {
this.props.attrs = {};
this.props.layout = 'row'; //Used to tell child nodes what layout to expect
},
getReactComponent() {
return Button;
},
inputs: {
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}
},
inputCss: {
backgroundColor: {
index: 100,

View File

@@ -31,6 +31,7 @@ const CheckBoxNode = {
]
},
initialize() {
this.props.attrs = {};
this.props.sizeMode = 'explicit';
this.props.id = 'input-' + guid();
this.props.checked = this._internal.checked = false;
@@ -94,6 +95,16 @@ const CheckBoxNode = {
this.flagOutputDirty('checked');
this._updateVisualState();
}
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}
},
inputCss: {

View File

@@ -31,6 +31,7 @@ const OptionsNode = {
]
},
initialize: function () {
this.props.attrs = {};
this._itemsChanged = () => {
this.forceUpdate();
};
@@ -90,6 +91,16 @@ const OptionsNode = {
this.flagOutputDirty('value');
}
}
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}
},
inputProps: {

View File

@@ -31,6 +31,7 @@ const RadioButtonNode = {
]
},
initialize() {
this.props.attrs = {};
this.props.sizeMode = 'explicit';
this.props.id = 'input-' + guid();
@@ -61,6 +62,16 @@ const RadioButtonNode = {
set(value) {
this.setStyle({ backgroundColor: value }, 'fill');
}
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}
},
inputProps: {

View File

@@ -27,6 +27,7 @@ const RangeNode = {
]
},
initialize() {
this.props.attrs = {};
this.props.sizeMode = 'contentHeight';
this.props.id = 'input-' + guid();
@@ -67,6 +68,16 @@ const RangeNode = {
set(value) {
this._setInputValue(value);
}
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}
},
outputs: {

View File

@@ -43,6 +43,7 @@ const TextInputNode = {
return TextInput;
},
initialize() {
this.props.attrs = {};
this.props.startValue = '';
this.props.id = this._internal.controlId = 'input-' + guid();
},
@@ -167,6 +168,16 @@ const TextInputNode = {
break;
}
}
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}
},
inputCss: {

View File

@@ -72,8 +72,10 @@ const Navigate = {
backCallback: (action, results) => {
this._internal.backResults = results;
for (var key in results) {
if (this.hasOutput('backResult-' + key)) this.flagOutputDirty('backResult-' + key);
for (const key in results) {
if (this.hasOutput('backResult-' + key)) {
this.flagOutputDirty('backResult-' + key);
}
}
if (action !== undefined) this.sendSignalOnOutput(action);
@@ -114,22 +116,23 @@ const Navigate = {
return;
}
if (name === 'target')
if (name === 'target') {
return this.registerInput(name, {
set: this.setTargetPageId.bind(this)
});
else if (name === 'transition')
} else if (name === 'transition') {
return this.registerInput(name, {
set: this.setTransition.bind(this)
});
else if (name.startsWith('tr-'))
} else if (name.startsWith('tr-')) {
return this.registerInput(name, {
set: this.setTransitionParam.bind(this, name.substring('tr-'.length))
});
else if (name.startsWith('pm-'))
} else if (name.startsWith('pm-')) {
return this.registerInput(name, {
set: this.setPageParam.bind(this, name.substring('pm-'.length))
});
}
},
registerOutputIfNeeded: function (name) {
if (this.hasOutput(name)) {
@@ -174,18 +177,24 @@ function setup(context, graphModel) {
if (Transitions[transition]) ports = ports.concat(Transitions[transition].ports(node.parameters));
}
// if(node.parameters['stack'] !== undefined) {
var pageStacks = graphModel.getNodesWithType('Page Stack');
var pageStack = pageStacks.find(
const pageStacks = graphModel.getNodesWithType('Page Stack');
const pageStack = pageStacks.find(
(ps) => (ps.parameters['name'] || 'Main') === (node.parameters['stack'] || 'Main')
);
if (pageStack !== undefined) {
var pages = pageStack.parameters['pages'];
const pages = pageStack.parameters['pages'];
if (pages !== undefined && pages.length > 0) {
ports.push({
plug: 'input',
type: { name: 'enum', enums: pages.map((p) => ({ label: p.label, value: p.id })), allowEditOnly: true },
type: {
name: 'enum',
enums: pages.map((p) => ({
label: p.label,
value: p.id
})),
allowEditOnly: true
},
group: 'General',
displayName: 'Target Page',
name: 'target',
@@ -193,14 +202,14 @@ function setup(context, graphModel) {
});
// See if there is a target page with component
var targetPageId = node.parameters['target'] || pages[0].id;
var targetComponentName = pageStack.parameters['pageComp-' + targetPageId];
const targetPageId = node.parameters['target'] || pages[0].id;
const targetComponentName = pageStack.parameters['pageComp-' + targetPageId];
if (targetComponentName !== undefined) {
const component = graphModel.components[targetComponentName];
if (component !== undefined) {
// Make all inputs of the component to inputs of this navigation node
for (var inputName in component.inputPorts) {
for (const inputName in component.inputPorts) {
ports.push({
name: 'pm-' + inputName,
displayName: inputName,
@@ -245,7 +254,6 @@ function setup(context, graphModel) {
}
}
}
// }
context.editorConnection.sendDynamicPorts(node.id, ports);
}

View File

@@ -43,6 +43,10 @@ const OpenFilePicker = {
input.accept = this._internal.acceptedFileTypes;
if (this._internal.capture) {
input.capture = this._internal.capture;
}
input.onchange = onChange;
input.click();
}
@@ -54,6 +58,14 @@ const OpenFilePicker = {
set(value) {
this._internal.acceptedFileTypes = value;
}
},
capture: {
group: 'General',
type: 'string',
displayName: 'Capture',
set(value) {
this._internal.capture = value;
}
}
},
outputs: {

View File

@@ -4,7 +4,7 @@ const Switch = {
name: 'Switch',
docs: 'https://docs.noodl.net/nodes/logic/switch',
category: 'Logic',
initialize: function () {
initialize() {
this._internal.state = false;
this._internal.initialized = false;
},
@@ -15,7 +15,7 @@ const Switch = {
on: {
displayName: 'On',
group: 'Change State',
valueChangedToTrue: function () {
valueChangedToTrue() {
if (this._internal.state === true) {
return;
}
@@ -27,7 +27,7 @@ const Switch = {
off: {
displayName: 'Off',
group: 'Change State',
valueChangedToTrue: function () {
valueChangedToTrue() {
if (this._internal.state === false) {
return;
}
@@ -39,7 +39,7 @@ const Switch = {
flip: {
displayName: 'Flip',
group: 'Change State',
valueChangedToTrue: function () {
valueChangedToTrue() {
this._internal.state = !this._internal.state;
this.flagOutputDirty('state');
this.emitSignals();
@@ -50,7 +50,7 @@ const Switch = {
displayName: 'State',
group: 'General',
default: false,
set: function (value) {
set(value) {
this._internal.state = !!value;
this.flagOutputDirty('state');
this.emitSignals();
@@ -61,10 +61,15 @@ const Switch = {
state: {
type: 'boolean',
displayName: 'Current State',
getter: function () {
getter() {
return this._internal.state;
}
},
switched: {
displayName: 'Switched',
type: 'signal',
group: 'Signals'
},
switchedToOn: {
displayName: 'Switched To On',
type: 'signal',
@@ -77,12 +82,13 @@ const Switch = {
}
},
prototypeExtensions: {
emitSignals: function () {
emitSignals() {
if (this._internal.state === true) {
this.sendSignalOnOutput('switchedToOn');
} else {
this.sendSignalOnOutput('switchedToOff');
}
this.sendSignalOnOutput('switched');
}
}
};

View File

@@ -1,18 +1,13 @@
'use strict';
const { Node, EdgeTriggeredInput } = require('@noodl/runtime');
const UserService = require('./userservice');
var LoginNodeDefinition = {
const LoginNodeDefinition = {
name: 'net.noodl.user.LogIn',
docs: 'https://docs.noodl.net/nodes/data/user/log-in',
displayNodeName: 'Log In',
category: 'Cloud Services',
color: 'data',
initialize: function () {
var internal = this._internal;
},
getInspectInfo() {},
outputs: {
success: {
type: 'signal',
@@ -28,7 +23,7 @@ var LoginNodeDefinition = {
type: 'string',
displayName: 'Error',
group: 'Error',
getter: function () {
getter() {
return this._internal.error;
}
}
@@ -37,7 +32,7 @@ var LoginNodeDefinition = {
login: {
displayName: 'Do',
group: 'Actions',
valueChangedToTrue: function () {
valueChangedToTrue() {
this.scheduleLogIn();
}
},
@@ -45,7 +40,7 @@ var LoginNodeDefinition = {
displayName: 'Username',
type: 'string',
group: 'General',
set: function (value) {
set(value) {
this._internal.username = value;
}
},
@@ -53,13 +48,13 @@ var LoginNodeDefinition = {
displayName: 'Password',
type: 'string',
group: 'General',
set: function (value) {
set(value) {
this._internal.password = value;
}
}
},
methods: {
setError: function (err) {
setError(err) {
this._internal.error = err;
this.flagOutputDirty('error');
this.sendSignalOnOutput('failure');
@@ -76,9 +71,7 @@ var LoginNodeDefinition = {
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
}
},
scheduleLogIn: function () {
const internal = this._internal;
scheduleLogIn() {
if (this.logInScheduled === true) return;
this.logInScheduled = true;
@@ -100,89 +93,7 @@ var LoginNodeDefinition = {
}
};
/*function updatePorts(nodeId, parameters, editorConnection, dbCollections) {
var ports = [];
ports.push({
name: 'collectionName',
displayName: "Class",
group: "General",
type: { name: 'enum', enums: (dbCollections !== undefined) ? dbCollections.map((c) => { return { value: c.name, label: c.name } }) : [], allowEditOnly: true },
plug: 'input'
})
if (parameters.collectionName && dbCollections) {
// Fetch ports from collection keys
var c = dbCollections.find((c) => c.name === parameters.collectionName);
if (c && c.schema && c.schema.properties) {
var props = c.schema.properties;
for (var key in props) {
var p = props[key];
if (ports.find((_p) => _p.name === key)) continue;
if(p.type === 'Relation') {
}
else { // Other schema type ports
const _typeMap = {
"String":"string",
"Boolean":"boolean",
"Number":"number",
"Date":"date"
}
ports.push({
type: {
name: _typeMap[p.type]?_typeMap[p.type]:'*',
},
plug: 'output',
group: 'Properties',
name: 'prop-' + key,
displayName: key,
})
ports.push({
type: 'signal',
plug: 'output',
group: 'Changed Events',
displayName: key+ ' Changed',
name: 'changed-' + key,
})
}
}
}
}
editorConnection.sendDynamicPorts(nodeId, ports);
}*/
module.exports = {
node: LoginNodeDefinition,
setup: function (context, graphModel) {
/* if (!context.editorConnection || !context.editorConnection.isRunningLocally()) {
return;
}
function _managePortsForNode(node) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('dbCollections'));
node.on("parameterUpdated", function (event) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('dbCollections'));
});
graphModel.on('metadataChanged.dbCollections', function (data) {
updatePorts(node.id, node.parameters, context.editorConnection, data);
})
}
graphModel.on("editorImportComplete", ()=> {
graphModel.on("nodeAdded.DbModel2", function (node) {
_managePortsForNode(node)
})
for(const node of graphModel.getNodesWithType('DbModel2')) {
_managePortsForNode(node)
}
})*/
}
setup(_context, _graphModel) {}
};

View File

@@ -1,18 +1,13 @@
'use strict';
const { Node, EdgeTriggeredInput } = require('@noodl/runtime');
const UserService = require('./userservice');
var LogOutNodeDefinition = {
const LogOutNodeDefinition = {
name: 'net.noodl.user.LogOut',
docs: 'https://docs.noodl.net/nodes/data/user/log-out',
displayNodeName: 'Log Out',
category: 'Cloud Services',
color: 'data',
initialize: function () {
var internal = this._internal;
},
getInspectInfo() {},
outputs: {
success: {
type: 'signal',
@@ -28,7 +23,7 @@ var LogOutNodeDefinition = {
type: 'string',
displayName: 'Error',
group: 'Error',
getter: function () {
getter() {
return this._internal.error;
}
}
@@ -37,13 +32,13 @@ var LogOutNodeDefinition = {
login: {
displayName: 'Do',
group: 'Actions',
valueChangedToTrue: function () {
valueChangedToTrue() {
this.scheduleLogOut();
}
}
},
methods: {
setError: function (err) {
setError(err) {
this._internal.error = err;
this.flagOutputDirty('error');
this.sendSignalOnOutput('failure');
@@ -60,9 +55,7 @@ var LogOutNodeDefinition = {
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
}
},
scheduleLogOut: function () {
const internal = this._internal;
scheduleLogOut() {
if (this.logOutScheduled === true) return;
this.logOutScheduled = true;
@@ -84,5 +77,5 @@ var LogOutNodeDefinition = {
module.exports = {
node: LogOutNodeDefinition,
setup: function (context, graphModel) {}
setup(_context, _graphModel) {}
};

View File

@@ -1,20 +1,17 @@
'use strict';
const { Node, EdgeTriggeredInput } = require('@noodl/runtime');
const UserService = require('./userservice');
var SignUpNodeDefinition = {
const SignUpNodeDefinition = {
name: 'net.noodl.user.SignUp',
docs: 'https://docs.noodl.net/nodes/data/user/sign-up',
displayNodeName: 'Sign Up',
category: 'Cloud Services',
color: 'data',
initialize: function () {
var internal = this._internal;
initialize() {
const internal = this._internal;
internal.userProperties = {};
},
getInspectInfo() {},
outputs: {
success: {
type: 'signal',
@@ -30,7 +27,7 @@ var SignUpNodeDefinition = {
type: 'string',
displayName: 'Error',
group: 'Error',
getter: function () {
getter() {
return this._internal.error;
}
}
@@ -39,7 +36,7 @@ var SignUpNodeDefinition = {
signup: {
displayName: 'Do',
group: 'Actions',
valueChangedToTrue: function () {
valueChangedToTrue() {
this.scheduleSignUp();
}
},
@@ -47,7 +44,7 @@ var SignUpNodeDefinition = {
displayName: 'Username',
type: 'string',
group: 'General',
set: function (value) {
set(value) {
this._internal.username = value;
}
},
@@ -55,7 +52,7 @@ var SignUpNodeDefinition = {
displayName: 'Password',
type: 'string',
group: 'General',
set: function (value) {
set(value) {
this._internal.password = value;
}
},
@@ -63,13 +60,13 @@ var SignUpNodeDefinition = {
displayName: 'Email',
type: 'string',
group: 'General',
set: function (value) {
set(value) {
this._internal.email = value;
}
}
},
methods: {
setError: function (err) {
setError(err) {
this._internal.error = err;
this.flagOutputDirty('error');
this.sendSignalOnOutput('failure');
@@ -86,7 +83,7 @@ var SignUpNodeDefinition = {
this.context.editorConnection.clearWarning(this.nodeScope.componentOwner.name, this.id, 'user-login-warning');
}
},
scheduleSignUp: function () {
scheduleSignUp() {
const internal = this._internal;
if (this.signUpScheduled === true) return;
@@ -109,23 +106,24 @@ var SignUpNodeDefinition = {
});
});
},
setUserProperty: function (name, value) {
setUserProperty(name, value) {
this._internal.userProperties[name] = value;
},
registerInputIfNeeded: function (name) {
registerInputIfNeeded(name) {
if (this.hasInput(name)) {
return;
}
if (name.startsWith('prop-'))
if (name.startsWith('prop-')) {
return this.registerInput(name, {
set: this.setUserProperty.bind(this, name.substring('prop-'.length))
});
}
}
}
};
function updatePorts(nodeId, parameters, editorConnection, systemCollections) {
function updatePorts(nodeId, _parameters, editorConnection, systemCollections) {
var ports = [];
if (systemCollections) {
@@ -169,7 +167,7 @@ function updatePorts(nodeId, parameters, editorConnection, systemCollections) {
module.exports = {
node: SignUpNodeDefinition,
setup: function (context, graphModel) {
setup(context, graphModel) {
if (!context.editorConnection || !context.editorConnection.isRunningLocally()) {
return;
}
@@ -177,7 +175,7 @@ module.exports = {
function _managePortsForNode(node) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('systemCollections'));
node.on('parameterUpdated', function (event) {
node.on('parameterUpdated', function (_event) {
updatePorts(node.id, node.parameters, context.editorConnection, graphModel.getMetaData('systemCollections'));
});

View File

@@ -23,7 +23,7 @@ const ColumnsNode = {
]
},
initialize() {
initialize() { this.props.attrs = {};
this.props.layoutString = '1 2 1';
this.props.minWidth = 0;
this.props.marginX = 16;
@@ -62,6 +62,16 @@ const ColumnsNode = {
);
}
this.forceUpdate();
}
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}

View File

@@ -11,6 +11,7 @@ const GroupNode = {
groupPriority: ['General', 'Style', 'Events', 'Mounted', 'Hover Events', 'Pointer Events', 'Focus', 'Scroll']
},
initialize() {
this.props.attrs = {};
this._internal = {
scrollElementDuration: 500,
scrollIndexDuration: 500,
@@ -143,6 +144,16 @@ const GroupNode = {
valueChangedToTrue() {
this.context.setNodeFocused(this, true);
}
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}
},
inputProps: {

View File

@@ -27,6 +27,7 @@ const ImageNode = {
]
},
initialize() {
this.props.attrs = {};
this.props.default = '';
},
getReactComponent() {
@@ -86,6 +87,16 @@ const ImageNode = {
this.props.dom.src = getAbsoluteUrl(url);
this.forceUpdate();
}
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}
},
inputProps: {
@@ -114,6 +125,12 @@ const ImageNode = {
propPath: 'dom',
type: 'signal',
group: 'Events'
},
onError: {
displayName: 'On Error',
propPath: 'dom',
type: 'signal',
group: 'Events'
}
}
};

View File

@@ -20,6 +20,9 @@ const TextNode = {
nodeDoubleClickAction: {
focusPort: 'text'
},
initialize() {
this.props.attrs = {};
},
getReactComponent() {
return Text;
},
@@ -135,6 +138,16 @@ const TextNode = {
break;
}
}
},
testId: {
index: 100009,
displayName: 'Test ID Attribute',
group: 'Advanced HTML',
type: 'string',
set(value) {
this.props.attrs["data-testid"] = value;
this.forceUpdate();
}
}
}
};

View File

@@ -72,18 +72,18 @@ import { getCurrentPlatform } from '../helper';
// NOTE: Getting error "Cannot set properties of null (setting 'dev')" here,
// It basically means that some package is not relative to this path.
console.log("--- Run 'npm install' ...");
if (platform === "darwin") {
if (platform === 'darwin') {
execSync(`npm install electron-notarize`, {
stdio: 'inherit',
env: process.env
})
});
}
execSync(`npm install --arch=${arch} --scope noodl-editor`, {
execSync(`npm install --arch=${arch} --scope fluxscape-editor`, {
stdio: 'inherit',
env: process.env
})
});
console.log("--- 'npm install' done!");
// NOTE: npm install --arch= does this too
@@ -99,13 +99,13 @@ import { getCurrentPlatform } from '../helper';
// Build: Replace "dugite"
// Build: Replace "desktop-trampoline"
console.log("--- Run 'npm run build' ...");
execSync('npx lerna exec --scope noodl-editor -- npm run build', {
execSync('npx lerna exec --scope fluxscape-editor -- npm run build', {
stdio: 'inherit',
env: {
...process.env,
TARGET_PLATFORM,
DISABLE_SIGNING,
CSC_NAME,
CSC_NAME
}
});
console.log("--- 'npm run build' done!");

View File

@@ -74,7 +74,7 @@ const cloudRuntimeProcess = attachStdio(
}
);
const editorProcess = attachStdio(exec('npx lerna exec --scope noodl-editor -- npm run start', processOptions), {
const editorProcess = attachStdio(exec('npx lerna exec --scope fluxscape-editor -- npm run start', processOptions), {
prefix: 'Editor',
color: ConsoleColor.FgCyan
});

View File

@@ -1,36 +1,27 @@
import path from "path";
import { execSync } from "child_process";
import { execSync } from 'child_process';
import path from 'path';
const CWD = path.join(__dirname, "..");
const LOCAL_GIT_DIRECTORY = path.join(
__dirname,
"..",
"node_modules",
"dugite",
"git"
);
const CWD = path.join(__dirname, '..');
const LOCAL_GIT_DIRECTORY = path.join(__dirname, '..', 'node_modules', 'dugite', 'git');
const LOCAL_GIT_TRAMPOLINE_DIRECTORY = path.join(
__dirname,
"..",
"node_modules",
"desktop-trampoline/build/Release/desktop-trampoline"
'..',
'node_modules',
'desktop-trampoline/build/Release/desktop-trampoline'
);
console.log("---");
console.log('---');
console.log(`> CWD: `, CWD);
console.log(`> LOCAL_GIT_DIRECTORY: `, LOCAL_GIT_DIRECTORY);
console.log(
`> LOCAL_GIT_TRAMPOLINE_DIRECTORY: `,
LOCAL_GIT_TRAMPOLINE_DIRECTORY
);
console.log("---");
console.log(`> LOCAL_GIT_TRAMPOLINE_DIRECTORY: `, LOCAL_GIT_TRAMPOLINE_DIRECTORY);
console.log('---');
execSync("npx lerna exec --scope noodl-editor -- npm run test", {
execSync('npx lerna exec --scope fluxscape-editor -- npm run test', {
cwd: CWD,
stdio: "inherit",
stdio: 'inherit',
env: {
...process.env,
LOCAL_GIT_DIRECTORY,
LOCAL_GIT_TRAMPOLINE_DIRECTORY,
},
LOCAL_GIT_TRAMPOLINE_DIRECTORY
}
});