mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
Compare commits
61 Commits
cursor/enh
...
experiment
Author | SHA1 | Date | |
---|---|---|---|
![]() |
9d7ddab67b | ||
![]() |
3c327c7193 | ||
![]() |
34e71908a1 | ||
![]() |
62485db64e | ||
![]() |
af8d0f2a52 | ||
![]() |
f6aab10d22 | ||
![]() |
69d6723f1c | ||
![]() |
704b157285 | ||
![]() |
f50ddb6872 | ||
![]() |
eb7d4662bc | ||
![]() |
1ec1a1e600 | ||
![]() |
ec16740520 | ||
![]() |
e26e239b1c | ||
![]() |
0e48539a99 | ||
![]() |
d8a8e759a0 | ||
![]() |
36c03a054e | ||
![]() |
313d60a315 | ||
![]() |
5dd2f85796 | ||
![]() |
7e844ae539 | ||
![]() |
78da8185e5 | ||
![]() |
6de8b5503d | ||
![]() |
ea4c9ff3fa | ||
![]() |
2762415729 | ||
![]() |
90a34bd7d7 | ||
![]() |
31364bc487 | ||
![]() |
42b23f0d29 | ||
![]() |
b485bd0007 | ||
![]() |
c7a1883e1b | ||
![]() |
701b1d54e4 | ||
![]() |
95864b41d8 | ||
![]() |
14a1a5d746 | ||
![]() |
eb5acecb84 | ||
![]() |
04b1309f50 | ||
![]() |
babf0153a1 | ||
![]() |
a7b27b4d2d | ||
![]() |
78c4f4b7a5 | ||
![]() |
96206aac29 | ||
![]() |
95a938d93b | ||
![]() |
0286ecd139 | ||
![]() |
297be166cd | ||
![]() |
e626acdd21 | ||
![]() |
bf1f33b57a | ||
![]() |
890719b292 | ||
![]() |
e4815a1cb6 | ||
![]() |
d51cc42a64 | ||
![]() |
39a41e486d | ||
![]() |
6e857629bf | ||
![]() |
67a81549d1 | ||
![]() |
5f1c879b4c | ||
![]() |
521cfe2f97 | ||
![]() |
e4cc5023f8 | ||
![]() |
5f983bef1c | ||
![]() |
3878e99d4f | ||
![]() |
c9086554e2 | ||
![]() |
69e207fab4 | ||
![]() |
bdc674eda3 | ||
![]() |
d28b0c650d | ||
![]() |
78681eaba2 | ||
![]() |
48fa608c17 | ||
![]() |
6acecae77d | ||
![]() |
0a30e1e870 |
@@ -18,13 +18,13 @@ jobs:
|
||||
# Build the **entire** app for macOS (including the GDevelop.js library).
|
||||
build-macos:
|
||||
macos:
|
||||
xcode: 16.4.0
|
||||
resource_class: m4pro.medium
|
||||
xcode: 14.2.0
|
||||
resource_class: macos.m1.large.gen1
|
||||
steps:
|
||||
- checkout
|
||||
# Install Rosetta for AWS CLI and disable TSO to speed up S3 uploads (https://support.circleci.com/hc/en-us/articles/19334402064027-Troubleshooting-slow-uploads-to-S3-for-jobs-using-an-m1-macOS-resource-class)
|
||||
- macos/install-rosetta
|
||||
# - run: sudo sysctl net.inet.tcp.tso=0
|
||||
- run: sudo sysctl net.inet.tcp.tso=0
|
||||
|
||||
# Install a recent version of npm to workaround a notarization issue because of a symlink made by npm: https://github.com/electron-userland/electron-builder/issues/7755
|
||||
# Node.js v20.14.0 comes with npm v10.7.0.
|
||||
@@ -88,35 +88,13 @@ jobs:
|
||||
- store_artifacts:
|
||||
path: newIDE/electron-app/dist
|
||||
|
||||
|
||||
# Upload artifacts (AWS)
|
||||
- run:
|
||||
name: Deploy to S3 (specific commit)
|
||||
command: |
|
||||
export PATH=~/.local/bin:$PATH
|
||||
for i in 1 2 3 4 5 6 7; do
|
||||
aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/ && break
|
||||
echo "Retry $i failed... retrying in 10 seconds"
|
||||
sleep 10
|
||||
done
|
||||
if [ $i -eq 7 ]; then
|
||||
echo "All retries for deployment failed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
command: export PATH=~/.local/bin:$PATH && aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/commit/$(git rev-parse HEAD)/
|
||||
- run:
|
||||
name: Deploy to S3 (latest)
|
||||
command: |
|
||||
export PATH=~/.local/bin:$PATH
|
||||
for i in 1 2 3 4 5 6 7; do
|
||||
aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/ && break
|
||||
echo "Retry $i failed... retrying in 10 seconds"
|
||||
sleep 10
|
||||
done
|
||||
if [ $i -eq 7 ]; then
|
||||
echo "All retries for deployment failed!" >&2
|
||||
exit 1
|
||||
fi
|
||||
command: export PATH=~/.local/bin:$PATH && aws s3 sync newIDE/electron-app/dist s3://gdevelop-releases/$(git rev-parse --abbrev-ref HEAD)/latest/
|
||||
|
||||
# Build the app for Linux (using a pre-built GDevelop.js library).
|
||||
build-linux:
|
||||
@@ -390,7 +368,7 @@ jobs:
|
||||
npm -v
|
||||
|
||||
Remove-Item package-lock.json
|
||||
|
||||
|
||||
$Env:REQUIRES_EXACT_LIBGD_JS_VERSION = "true"
|
||||
|
||||
npm install
|
||||
@@ -447,10 +425,10 @@ jobs:
|
||||
|
||||
$Env:GD_SIGNTOOL_THUMBPRINT = ''
|
||||
|
||||
$Env:GD_SIGNTOOL_SUBJECT_NAME = ''
|
||||
|
||||
$Env:GD_SIGNTOOL_SUBJECT_NAME = ''
|
||||
|
||||
$Env:CSC_LINK = ''
|
||||
|
||||
|
||||
$Env:CSC_KEY_PASSWORD = ''
|
||||
|
||||
node scripts/build.js --skip-app-build --win appx --publish=never
|
||||
@@ -467,16 +445,16 @@ jobs:
|
||||
name: Install AWS CLI
|
||||
command: |
|
||||
# Install the CLI for the current user
|
||||
|
||||
|
||||
pip install --quiet --upgrade --user awscli
|
||||
|
||||
|
||||
# Add the user-Scripts dir to PATH for this step and the next.
|
||||
|
||||
|
||||
$binDir = (python -m site --user-base) + "\Scripts"
|
||||
$Env:Path += ";$binDir"
|
||||
|
||||
# Sanity check:
|
||||
aws --version
|
||||
aws --version
|
||||
|
||||
# Upload artifacts (S3)
|
||||
- run:
|
||||
|
@@ -1,130 +0,0 @@
|
||||
# AI Project Restore Implementation
|
||||
|
||||
## Overview
|
||||
This implementation provides automatic project saving and restoration capabilities for AI agent requests, leveraging GDevelop's existing cloud save features and version history system.
|
||||
|
||||
## Key Features
|
||||
|
||||
### 1. Automatic Project Save Before AI Agent Requests
|
||||
- When a user starts a new AI agent request, the project is automatically saved to create a cloud version
|
||||
- This only works for cloud projects (projects saved to GDevelop Cloud)
|
||||
- The current version ID is captured and stored with the AI request
|
||||
|
||||
### 2. Version-Based Restoration
|
||||
- Uses GDevelop's existing cloud version system instead of custom serialization
|
||||
- Leverages the `onOpenCloudProjectOnSpecificVersion` function from MainFrame
|
||||
- Provides seamless restoration to the exact state before AI agent modifications
|
||||
|
||||
### 3. Smart UI Integration
|
||||
- Restore button appears at the top of AI agent chats that have a stored initial version
|
||||
- Only visible for cloud projects with stored version information
|
||||
- Button shows loading state during restoration process
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### Backend API Changes
|
||||
- Extended `AiRequest` type to include `initialProjectVersionId?: string | null`
|
||||
- This field stores the cloud project version ID captured before starting the agent
|
||||
|
||||
### Frontend Changes
|
||||
|
||||
#### 1. AskAiEditorContainer.js
|
||||
- Added `onOpenCloudProjectOnSpecificVersion` prop to enable version restoration
|
||||
- Modified AI request creation to save project and capture version ID for cloud projects
|
||||
- Added `onRestoreInitialProject` callback that uses cloud version system
|
||||
- Only attempts version capture for cloud projects (`storageProvider.internalName === 'Cloud'`)
|
||||
|
||||
#### 2. AiRequestChat/index.js
|
||||
- Added restore button UI at the top of agent chats
|
||||
- Added `isCloudProject` prop to control button visibility
|
||||
- Added loading state for restore operation
|
||||
- Proper error handling during restoration
|
||||
|
||||
#### 3. EditorFunctions/index.js
|
||||
- Extended `EditorCallbacks` type to include optional `onSave` function
|
||||
- Enables AI components to trigger project saves when needed
|
||||
|
||||
#### 4. MainFrame/index.js
|
||||
- Added `onOpenCloudProjectOnSpecificVersion` to editor props
|
||||
- This connects the AI editor to the existing version restoration system
|
||||
|
||||
## User Experience
|
||||
|
||||
### Starting an Agent Request
|
||||
1. User opens AI agent and submits a request
|
||||
2. System automatically saves the current project (creates a new version)
|
||||
3. Version ID is stored with the AI request for later restoration
|
||||
4. AI agent proceeds with modifications
|
||||
|
||||
### Restoring to Initial State
|
||||
1. User sees "Click here to restore the project as it was at the beginning" button
|
||||
2. Clicking the button triggers cloud version restoration
|
||||
3. Project is restored to the exact state before AI agent started
|
||||
4. All changes made by the AI agent are discarded
|
||||
|
||||
## Technical Advantages
|
||||
|
||||
### 1. Leverages Existing Infrastructure
|
||||
- Uses GDevelop's mature cloud save and version system
|
||||
- No custom serialization/deserialization code needed
|
||||
- Inherits all cloud storage reliability and error handling
|
||||
|
||||
### 2. Scalable and Reliable
|
||||
- Cloud versions are professionally managed and backed up
|
||||
- No local storage limitations or browser storage issues
|
||||
- Consistent across different devices and sessions
|
||||
|
||||
### 3. Version History Integration
|
||||
- Restored versions appear in the project's version history
|
||||
- Users can access version history features for AI-generated content
|
||||
- Seamless integration with existing version management workflow
|
||||
|
||||
## Limitations and Considerations
|
||||
|
||||
### 1. Cloud Projects Only
|
||||
- Feature only works for projects saved to GDevelop Cloud
|
||||
- Local projects cannot use this restoration feature
|
||||
- Clear messaging is provided when feature is unavailable
|
||||
|
||||
### 2. Version Storage
|
||||
- Relies on cloud project version creation during save
|
||||
- Version IDs are stored locally in the AI request metadata
|
||||
- If local storage is cleared, version reference may be lost
|
||||
|
||||
### 3. Network Dependency
|
||||
- Restoration requires internet connection for cloud access
|
||||
- Standard cloud storage network limitations apply
|
||||
|
||||
## Error Handling
|
||||
|
||||
### 1. Save Failures
|
||||
- If initial save fails, AI request continues without version storage
|
||||
- User is informed that restoration won't be available
|
||||
- Graceful degradation ensures AI functionality remains available
|
||||
|
||||
### 2. Restoration Failures
|
||||
- Comprehensive error logging for debugging
|
||||
- UI provides feedback during restoration process
|
||||
- Button disabled during restoration to prevent conflicts
|
||||
|
||||
### 3. Non-Cloud Projects
|
||||
- Restore functionality hidden for non-cloud projects
|
||||
- Clear console warnings when attempting unsupported operations
|
||||
- No impact on existing AI functionality for local projects
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### 1. Backend Integration
|
||||
- Could extend backend API to store `initialProjectVersionId` server-side
|
||||
- Would enable restoration across sessions and devices
|
||||
- Currently relies on local client-side storage
|
||||
|
||||
### 2. Local Project Support
|
||||
- Could implement local project snapshots using browser storage
|
||||
- Would require custom serialization for non-cloud projects
|
||||
- Currently prioritizes cloud projects for reliability
|
||||
|
||||
### 3. Enhanced UI
|
||||
- Could add confirmation dialogs for restoration
|
||||
- Might include preview of changes before restoration
|
||||
- Could integrate with version history UI components
|
@@ -37,8 +37,8 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.SetIcon("res/actions/position24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Angle"))
|
||||
.SetIcon("res/actions/direction24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size")).SetIcon(
|
||||
"res/actions/scale24_black.png");
|
||||
extension.AddInstructionOrExpressionGroupMetadata(_("Size"))
|
||||
.SetIcon("res/actions/scale24_black.png");
|
||||
|
||||
gd::ObjectMetadata& obj = extension.AddObject<gd::ObjectConfiguration>(
|
||||
"", _("Base object"), _("Base object"), "res/objeticon24.png");
|
||||
@@ -235,8 +235,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
|
||||
obj.AddAction("SetAngle",
|
||||
_("Angle"),
|
||||
_("Change the angle of rotation of an object (in degrees). For "
|
||||
"3D objects, this is the rotation around the Z axis."),
|
||||
_("Change the angle of rotation of an object (in degrees)."),
|
||||
_("the angle"),
|
||||
_("Angle"),
|
||||
"res/actions/direction24_black.png",
|
||||
@@ -251,8 +250,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
obj.AddAction("Rotate",
|
||||
_("Rotate"),
|
||||
_("Rotate an object, clockwise if the speed is positive, "
|
||||
"counterclockwise otherwise. For 3D objects, this is the "
|
||||
"rotation around the Z axis."),
|
||||
"counterclockwise otherwise."),
|
||||
_("Rotate _PARAM0_ at speed _PARAM1_ deg/second"),
|
||||
_("Angle"),
|
||||
"res/actions/rotate24_black.png",
|
||||
@@ -636,8 +634,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
|
||||
obj.AddCondition("Angle",
|
||||
_("Angle"),
|
||||
_("Compare the angle, in degrees, of the specified object. "
|
||||
"For 3D objects, this is the angle around the Z axis."),
|
||||
_("Compare the angle of the specified object."),
|
||||
_("the angle (in degrees)"),
|
||||
_("Angle"),
|
||||
"res/conditions/direction24_black.png",
|
||||
@@ -838,13 +835,14 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
.MarkAsAdvanced()
|
||||
.SetRelevantForLayoutEventsOnly();
|
||||
|
||||
obj.AddAction("PushBooleanToObjectVariable",
|
||||
_("Add value to object array variable"),
|
||||
_("Adds a boolean to the end of an object array variable."),
|
||||
_("Add value _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
|
||||
_("Variables ❯ Arrays and structures"),
|
||||
"res/actions/var24.png",
|
||||
"res/actions/var.png")
|
||||
obj.AddAction(
|
||||
"PushBooleanToObjectVariable",
|
||||
_("Add value to object array variable"),
|
||||
_("Adds a boolean to the end of an object array variable."),
|
||||
_("Add value _PARAM2_ to array variable _PARAM1_ of _PARAM0_"),
|
||||
_("Variables ❯ Arrays and structures"),
|
||||
"res/actions/var24.png",
|
||||
"res/actions/var.png")
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("objectvar", _("Array variable"))
|
||||
.AddParameter("trueorfalse", _("Boolean to add"))
|
||||
@@ -1270,8 +1268,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
|
||||
obj.AddExpression("Angle",
|
||||
_("Angle"),
|
||||
_("Current angle, in degrees, of the object. For 3D "
|
||||
"objects, this is the angle around the Z axis."),
|
||||
_("Current angle, in degrees, of the object"),
|
||||
_("Angle"),
|
||||
"res/actions/direction_black.png")
|
||||
.AddParameter("object", _("Object"));
|
||||
@@ -1574,9 +1571,7 @@ void GD_CORE_API BuiltinExtensionsImplementer::ImplementsBaseObjectExtension(
|
||||
extension
|
||||
.AddAction("Create",
|
||||
_("Create an object"),
|
||||
_("Create an instance of the object at the specified position."
|
||||
"The created object instance will be available for the next "
|
||||
"actions and sub-events."),
|
||||
_("Create an object at specified position"),
|
||||
_("Create object _PARAM1_ at position _PARAM2_;_PARAM3_ "
|
||||
"(layer: _PARAM4_)"),
|
||||
"",
|
||||
|
@@ -72,8 +72,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
|
||||
extension
|
||||
.AddExpression("normalize",
|
||||
_("Normalize a value between `min` and `max` to a value "
|
||||
"between 0 and 1."),
|
||||
_("Normalize a value between `min` and `max` to a value between 0 and 1."),
|
||||
_("Remap a value between 0 and 1."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
@@ -125,8 +124,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
extension
|
||||
.AddExpression("mod",
|
||||
_("Modulo"),
|
||||
_("Compute \"x mod y\". GDevelop does NOT support the \% "
|
||||
"operator. Use this mod(x, y) function instead."),
|
||||
_("x mod y"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("x (as in x mod y)"))
|
||||
@@ -186,8 +184,11 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"asinh", _("Arcsine"), _("Arcsine"), "", "res/mathfunction.png")
|
||||
.AddExpression("asinh",
|
||||
_("Arcsine"),
|
||||
_("Arcsine"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -217,8 +218,11 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"cbrt", _("Cube root"), _("Cube root"), "", "res/mathfunction.png")
|
||||
.AddExpression("cbrt",
|
||||
_("Cube root"),
|
||||
_("Cube root"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -256,13 +260,12 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"), "", true);
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"cos",
|
||||
_("Cosine"),
|
||||
_("Cosine of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression("cos",
|
||||
_("Cosine"),
|
||||
_("Cosine of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -290,20 +293,29 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"int", _("Round"), _("Round a number"), "", "res/mathfunction.png")
|
||||
.AddExpression("int",
|
||||
_("Round"),
|
||||
_("Round a number"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.SetHidden()
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"rint", _("Round"), _("Round a number"), "", "res/mathfunction.png")
|
||||
.AddExpression("rint",
|
||||
_("Round"),
|
||||
_("Round a number"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.SetHidden()
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"round", _("Round"), _("Round a number"), "", "res/mathfunction.png")
|
||||
.AddExpression("round",
|
||||
_("Round"),
|
||||
_("Round a number"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -312,8 +324,8 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
_("Round a number to the Nth decimal place"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Number to Round"))
|
||||
.AddParameter("expression", _("Decimal Places"), "", true);
|
||||
.AddParameter("expression", _("Expression"))
|
||||
.AddParameter("expression", _("Expression"), "", true);
|
||||
|
||||
extension
|
||||
.AddExpression("exp",
|
||||
@@ -324,13 +336,19 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"log", _("Logarithm"), _("Logarithm"), "", "res/mathfunction.png")
|
||||
.AddExpression("log",
|
||||
_("Logarithm"),
|
||||
_("Logarithm"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"ln", _("Logarithm"), _("Logarithm"), "", "res/mathfunction.png")
|
||||
.AddExpression("ln",
|
||||
_("Logarithm"),
|
||||
_("Logarithm"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.SetHidden()
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
@@ -369,8 +387,11 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("The exponent (n in x^n)"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"sec", _("Secant"), _("Secant"), "", "res/mathfunction.png")
|
||||
.AddExpression("sec",
|
||||
_("Secant"),
|
||||
_("Secant"),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -382,13 +403,12 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"sin",
|
||||
_("Sine"),
|
||||
_("Sine of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression("sin",
|
||||
_("Sine"),
|
||||
_("Sine of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `sin(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -408,13 +428,12 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"tan",
|
||||
_("Tangent"),
|
||||
_("Tangent of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `tan(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression("tan",
|
||||
_("Tangent"),
|
||||
_("Tangent of an angle (in radian). "
|
||||
"If you want to use degrees, use`ToRad`: `tan(ToRad(45))`."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Expression"));
|
||||
|
||||
extension
|
||||
@@ -444,28 +463,26 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
.AddParameter("expression", _("x (in a+(b-a)*x)"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"XFromAngleAndDistance",
|
||||
_("X position from angle and distance"),
|
||||
_("Compute the X position when given an angle and distance "
|
||||
"relative to the origin (0;0). This is also known as "
|
||||
"getting the cartesian coordinates of a 2D vector, using "
|
||||
"its polar coordinates."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression("XFromAngleAndDistance",
|
||||
_("X position from angle and distance"),
|
||||
_("Compute the X position when given an angle and distance "
|
||||
"relative to the origin (0;0). This is also known as "
|
||||
"getting the cartesian coordinates of a 2D vector, using "
|
||||
"its polar coordinates."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Angle, in degrees"))
|
||||
.AddParameter("expression", _("Distance"));
|
||||
|
||||
extension
|
||||
.AddExpression(
|
||||
"YFromAngleAndDistance",
|
||||
_("Y position from angle and distance"),
|
||||
_("Compute the Y position when given an angle and distance "
|
||||
"relative to the origin (0;0). This is also known as "
|
||||
"getting the cartesian coordinates of a 2D vector, using "
|
||||
"its polar coordinates."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddExpression("YFromAngleAndDistance",
|
||||
_("Y position from angle and distance"),
|
||||
_("Compute the Y position when given an angle and distance "
|
||||
"relative to the origin (0;0). This is also known as "
|
||||
"getting the cartesian coordinates of a 2D vector, using "
|
||||
"its polar coordinates."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Angle, in degrees"))
|
||||
.AddParameter("expression", _("Distance"));
|
||||
|
||||
@@ -480,8 +497,7 @@ BuiltinExtensionsImplementer::ImplementsMathematicalToolsExtension(
|
||||
extension
|
||||
.AddExpression("lerpAngle",
|
||||
_("Lerp (Linear interpolation) between two angles"),
|
||||
_("Linearly interpolates between two angles (in degrees) "
|
||||
"by taking the shortest direction around the circle."),
|
||||
_("Linearly interpolates between two angles (in degrees) by taking the shortest direction around the circle."),
|
||||
"",
|
||||
"res/mathfunction.png")
|
||||
.AddParameter("expression", _("Starting angle, in degrees"))
|
||||
|
@@ -250,28 +250,25 @@ void CustomObjectConfiguration::ExposeResources(gd::ArbitraryResourceWorker& wor
|
||||
}
|
||||
const auto &eventsBasedObject = project->GetEventsBasedObject(GetType());
|
||||
|
||||
if (IsForcedToOverrideEventsBasedObjectChildrenConfiguration()) {
|
||||
for (auto &childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
auto &configuration = GetChildObjectConfiguration(childObject->GetName());
|
||||
configuration.ExposeResources(worker);
|
||||
}
|
||||
}
|
||||
else if (eventsBasedObject.GetVariants().HasVariantNamed(variantName)) {
|
||||
for (auto &childObject : eventsBasedObject.GetVariants()
|
||||
.GetVariant(variantName)
|
||||
.GetObjects()
|
||||
.GetObjects()) {
|
||||
childObject->GetConfiguration().ExposeResources(worker);
|
||||
}
|
||||
} else if (isMarkedAsOverridingEventsBasedObjectChildrenConfiguration) {
|
||||
if (isMarkedAsOverridingEventsBasedObjectChildrenConfiguration) {
|
||||
for (auto &childObject : eventsBasedObject.GetObjects().GetObjects()) {
|
||||
auto &configuration = GetChildObjectConfiguration(childObject->GetName());
|
||||
configuration.ExposeResources(worker);
|
||||
}
|
||||
} else {
|
||||
for (auto &childObject :
|
||||
eventsBasedObject.GetDefaultVariant().GetObjects().GetObjects()) {
|
||||
childObject->GetConfiguration().ExposeResources(worker);
|
||||
if (variantName.empty() ||
|
||||
!eventsBasedObject.GetVariants().HasVariantNamed(variantName)) {
|
||||
for (auto &childObject :
|
||||
eventsBasedObject.GetDefaultVariant().GetObjects().GetObjects()) {
|
||||
childObject->GetConfiguration().ExposeResources(worker);
|
||||
}
|
||||
} else {
|
||||
for (auto &childObject : eventsBasedObject.GetVariants()
|
||||
.GetVariant(variantName)
|
||||
.GetObjects()
|
||||
.GetObjects()) {
|
||||
childObject->GetConfiguration().ExposeResources(worker);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -78,15 +78,6 @@ public:
|
||||
variantName = variantName_;
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy events-based objects don't have any instance in their default
|
||||
* variant since there wasn't a graphical editor at the time. In this case,
|
||||
* the editor doesn't allow to choose a variant, but a variant may have stayed
|
||||
* after a user rolled back the extension. This variant must be ignored.
|
||||
*
|
||||
* @return true when its events-based object doesn't have any initial
|
||||
* instance.
|
||||
*/
|
||||
bool IsForcedToOverrideEventsBasedObjectChildrenConfiguration() const;
|
||||
|
||||
bool IsMarkedAsOverridingEventsBasedObjectChildrenConfiguration() const {
|
||||
|
@@ -365,8 +365,6 @@ class GD_CORE_API InitialInstance {
|
||||
* the same initial instance between serialization.
|
||||
*/
|
||||
InitialInstance& ResetPersistentUuid();
|
||||
|
||||
const gd::String& GetPersistentUuid() const { return persistentUuid; }
|
||||
///@}
|
||||
|
||||
private:
|
||||
|
@@ -34,6 +34,7 @@ namespace gdjs {
|
||||
objectData: gdjs.Object3DData & gdjs.CustomObjectConfiguration
|
||||
) {
|
||||
super(parent, objectData);
|
||||
this._renderer.reinitialize(this, parent);
|
||||
}
|
||||
|
||||
protected override _createRender() {
|
||||
|
@@ -44,7 +44,10 @@ namespace gdjs {
|
||||
) {
|
||||
this._object = object;
|
||||
this._isContainerDirty = true;
|
||||
this._threeGroup.clear();
|
||||
const layer = parent.getLayer('');
|
||||
if (layer) {
|
||||
layer.getRenderer().add3DRendererObject(this._threeGroup);
|
||||
}
|
||||
}
|
||||
|
||||
_updateThreeGroup() {
|
||||
|
@@ -14,7 +14,6 @@ describe('gdjs.AnchorRuntimeBehavior', () => {
|
||||
effects: [],
|
||||
content: {},
|
||||
childrenContent: {},
|
||||
isInnerAreaFollowingParentSize: false,
|
||||
});
|
||||
runtimeScene.addObject(customObject);
|
||||
customObject.setPosition(500, 250);
|
||||
|
@@ -35,32 +35,25 @@ void DeclareDraggableBehaviorExtension(gd::PlatformExtension& extension) {
|
||||
std::make_shared<DraggableBehavior>(),
|
||||
std::shared_ptr<gd::BehaviorsSharedData>());
|
||||
|
||||
aut.AddCondition(
|
||||
"Dragged",
|
||||
_("Being dragged"),
|
||||
_("Check if the object is being dragged. This means the mouse button "
|
||||
"or touch is pressed on it. When the mouse button or touch is "
|
||||
"released, the object is no longer being considered dragged (use "
|
||||
"the condition \"Was just dropped\" to check when the dragging is "
|
||||
"ending)."),
|
||||
_("_PARAM0_ is being dragged"),
|
||||
_("Draggable"),
|
||||
"CppPlatform/Extensions/draggableicon24.png",
|
||||
"CppPlatform/Extensions/draggableicon16.png")
|
||||
aut.AddCondition("Dragged",
|
||||
_("Being dragged"),
|
||||
_("Check if the object is being dragged."),
|
||||
_("_PARAM0_ is being dragged"),
|
||||
_("Draggable"),
|
||||
"CppPlatform/Extensions/draggableicon24.png",
|
||||
"CppPlatform/Extensions/draggableicon16.png")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "Draggable")
|
||||
.SetFunctionName("IsDragged");
|
||||
|
||||
aut.AddCondition(
|
||||
"Dropped",
|
||||
_("Was just dropped"),
|
||||
_("Check if the object was just dropped after being dragged (the "
|
||||
"mouse button or touch was just released this frame)."),
|
||||
_("_PARAM0_ was just dropped"),
|
||||
_("Draggable"),
|
||||
"CppPlatform/Extensions/draggableicon24.png",
|
||||
"CppPlatform/Extensions/draggableicon16.png")
|
||||
aut.AddCondition("Dropped",
|
||||
_("Was just dropped"),
|
||||
_("Check if the object was just dropped after being dragged."),
|
||||
_("_PARAM0_ was just dropped"),
|
||||
_("Draggable"),
|
||||
"CppPlatform/Extensions/draggableicon24.png",
|
||||
"CppPlatform/Extensions/draggableicon16.png")
|
||||
|
||||
.AddParameter("object", _("Object"))
|
||||
.AddParameter("behavior", _("Behavior"), "Draggable")
|
||||
|
@@ -818,7 +818,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('yesorno', _('Treat as bullet'), '', false)
|
||||
.addParameter('yesorno', _('Treat as bullet?'), '', false)
|
||||
.setDefaultValue('false')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setBullet');
|
||||
@@ -852,7 +852,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('yesorno', _('Fixed rotation'), '', false)
|
||||
.addParameter('yesorno', _('Fixed rotation?'), '', false)
|
||||
.setDefaultValue('false')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setFixedRotation');
|
||||
@@ -886,7 +886,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('yesorno', _('Can sleep'), '', false)
|
||||
.addParameter('yesorno', _('Can sleep?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setSleepingAllowed');
|
||||
@@ -1296,7 +1296,7 @@ module.exports = {
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('expression', _('Layer (1 - 16)'))
|
||||
.addParameter('yesorno', _('Enable'), '', false)
|
||||
.addParameter('yesorno', _('Enable?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('enableLayer');
|
||||
@@ -1332,7 +1332,7 @@ module.exports = {
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('expression', _('Mask (1 - 16)'))
|
||||
.addParameter('yesorno', _('Enable'), '', false)
|
||||
.addParameter('yesorno', _('Enable?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('enableMask');
|
||||
@@ -2409,7 +2409,7 @@ module.exports = {
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('expression', _('Joint ID'))
|
||||
.addParameter('yesorno', _('Enable'))
|
||||
.addParameter('yesorno', _('Enable?'))
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('enableRevoluteJointLimits');
|
||||
|
||||
@@ -2488,7 +2488,7 @@ module.exports = {
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('expression', _('Joint ID'))
|
||||
.addParameter('yesorno', _('Enable'))
|
||||
.addParameter('yesorno', _('Enable?'))
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('enableRevoluteJointMotor');
|
||||
|
||||
@@ -2727,7 +2727,7 @@ module.exports = {
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('expression', _('Joint ID'))
|
||||
.addParameter('yesorno', _('Enable'))
|
||||
.addParameter('yesorno', _('Enable?'))
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('enablePrismaticJointLimits');
|
||||
|
||||
@@ -2806,7 +2806,7 @@ module.exports = {
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('expression', _('Joint ID'))
|
||||
.addParameter('yesorno', _('Enable'))
|
||||
.addParameter('yesorno', _('Enable?'))
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('enablePrismaticJointMotor');
|
||||
|
||||
@@ -3486,7 +3486,7 @@ module.exports = {
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics2Behavior')
|
||||
.addParameter('expression', _('Joint ID'))
|
||||
.addParameter('yesorno', _('Enable'))
|
||||
.addParameter('yesorno', _('Enable?'))
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('enableWheelJointMotor');
|
||||
|
||||
|
@@ -274,7 +274,7 @@ module.exports = {
|
||||
.setLabel('Fixed Rotation')
|
||||
.setDescription(
|
||||
_(
|
||||
"If enabled, the object won't rotate and will stay at the same angle."
|
||||
"If enabled, the object won't rotate and will stay at the same angle. Useful for characters for example."
|
||||
)
|
||||
)
|
||||
.setGroup(_('Movement'));
|
||||
@@ -845,7 +845,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics3DBehavior')
|
||||
.addParameter('yesorno', _('Treat as bullet'), '', false)
|
||||
.addParameter('yesorno', _('Treat as bullet?'), '', false)
|
||||
.setDefaultValue('false')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setBullet');
|
||||
@@ -870,7 +870,7 @@ module.exports = {
|
||||
'SetFixedRotation',
|
||||
_('Fixed rotation'),
|
||||
_(
|
||||
"Enable or disable an object fixed rotation. If enabled the object won't be able to rotate. This action has no effect on characters."
|
||||
"Enable or disable an object fixed rotation. If enabled the object won't be able to rotate."
|
||||
),
|
||||
_('Set _PARAM0_ fixed rotation: _PARAM2_'),
|
||||
_('Dynamics'),
|
||||
@@ -879,7 +879,7 @@ module.exports = {
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics3DBehavior')
|
||||
.addParameter('yesorno', _('Fixed rotation'), '', false)
|
||||
.addParameter('yesorno', _('Fixed rotation?'), '', false)
|
||||
.setDefaultValue('false')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('setFixedRotation');
|
||||
@@ -1054,7 +1054,7 @@ module.exports = {
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics3DBehavior')
|
||||
.addParameter('expression', _('Layer (1 - 8)'))
|
||||
.addParameter('yesorno', _('Enable'), '', false)
|
||||
.addParameter('yesorno', _('Enable?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('enableLayer');
|
||||
@@ -1090,7 +1090,7 @@ module.exports = {
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics3DBehavior')
|
||||
.addParameter('expression', _('Mask (1 - 8)'))
|
||||
.addParameter('yesorno', _('Enable'), '', false)
|
||||
.addParameter('yesorno', _('Enable?'), '', false)
|
||||
.setDefaultValue('true')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('enableMask');
|
||||
@@ -1270,7 +1270,7 @@ module.exports = {
|
||||
.addParameter('expression', _('Application point on Z axis'))
|
||||
.setParameterLongDescription(
|
||||
_(
|
||||
'Use `MassCenterX`, `MassCenterY` and `MassCenterZ` expressions to avoid any rotation.'
|
||||
'Use `MassCenterX` and `MassCenterY` expressions to avoid any rotation.'
|
||||
)
|
||||
)
|
||||
.getCodeExtraInformation()
|
||||
@@ -1544,19 +1544,6 @@ module.exports = {
|
||||
.addParameter('behavior', _('Behavior'), 'Physics3DBehavior')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('getMassCenterY');
|
||||
|
||||
aut
|
||||
.addExpression(
|
||||
'MassCenterZ',
|
||||
_('Mass center Z'),
|
||||
_('Mass center Z'),
|
||||
'',
|
||||
'JsPlatform/Extensions/physics3d.svg'
|
||||
)
|
||||
.addParameter('object', _('Object'), '', false)
|
||||
.addParameter('behavior', _('Behavior'), 'Physics3DBehavior')
|
||||
.getCodeExtraInformation()
|
||||
.setFunctionName('getMassCenterZ');
|
||||
}
|
||||
// Collision
|
||||
extension
|
||||
|
@@ -927,7 +927,9 @@ namespace gdjs {
|
||||
const angularVelocityY = angularVelocity.GetY();
|
||||
const angularVelocityZ = angularVelocity.GetZ();
|
||||
|
||||
this.bodyUpdater.destroyBody();
|
||||
let bodyID = this._body.GetID();
|
||||
bodyInterface.RemoveBody(bodyID);
|
||||
bodyInterface.DestroyBody(bodyID);
|
||||
this._contactsEndedThisFrame.length = 0;
|
||||
this._contactsStartedThisFrame.length = 0;
|
||||
this._currentContacts.length = 0;
|
||||
@@ -936,7 +938,7 @@ namespace gdjs {
|
||||
if (!this._body) {
|
||||
return;
|
||||
}
|
||||
const bodyID = this._body.GetID();
|
||||
bodyID = this._body.GetID();
|
||||
bodyInterface.SetLinearVelocity(
|
||||
bodyID,
|
||||
this.getVec3(linearVelocityX, linearVelocityY, linearVelocityZ)
|
||||
|
@@ -733,7 +733,7 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
setWheelOffsetZ(wheelOffsetZ: float): void {
|
||||
this._wheelOffsetZ = wheelOffsetZ;
|
||||
this._wheelOffsetY = wheelOffsetZ;
|
||||
this._updateWheels();
|
||||
}
|
||||
|
||||
@@ -783,11 +783,11 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
hasFrontWheelDrive(): boolean {
|
||||
return this._hasFrontWheelDrive;
|
||||
return this._hasBackWheelDrive;
|
||||
}
|
||||
|
||||
setFrontWheelDrive(hasFrontWheelDrive: boolean): void {
|
||||
this._hasFrontWheelDrive = hasFrontWheelDrive;
|
||||
this._hasBackWheelDrive = hasFrontWheelDrive;
|
||||
this.invalidateShape();
|
||||
}
|
||||
|
||||
|
@@ -14,7 +14,6 @@ namespace gdjs {
|
||||
animatable?: SpriteAnimationData[];
|
||||
variant: string;
|
||||
childrenContent: { [objectName: string]: ObjectConfiguration & any };
|
||||
isInnerAreaFollowingParentSize: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -112,19 +111,9 @@ namespace gdjs {
|
||||
name: '',
|
||||
};
|
||||
}
|
||||
// Legacy events-based objects don't have any instance in their default
|
||||
// variant since there wasn't a graphical editor at the time.
|
||||
// In this case, the editor doesn't allow to choose a variant, but a
|
||||
// variant may have stayed after a user rolled back the extension.
|
||||
// This variant must be ignored to match what the editor shows.
|
||||
const isForcedToOverrideEventsBasedObjectChildrenConfiguration =
|
||||
eventsBasedObjectData.defaultVariant.instances.length == 0;
|
||||
let usedVariantData: EventsBasedObjectVariantData =
|
||||
eventsBasedObjectData.defaultVariant;
|
||||
if (
|
||||
customObjectData.variant &&
|
||||
!isForcedToOverrideEventsBasedObjectChildrenConfiguration
|
||||
) {
|
||||
if (customObjectData.variant) {
|
||||
for (
|
||||
let variantIndex = 0;
|
||||
variantIndex < eventsBasedObjectData.variants.length;
|
||||
@@ -165,12 +154,10 @@ namespace gdjs {
|
||||
override reinitialize(objectData: ObjectData & CustomObjectConfiguration) {
|
||||
super.reinitialize(objectData);
|
||||
|
||||
this._reinitializeRenderer();
|
||||
this._initializeFromObjectData(objectData);
|
||||
this._reinitializeRenderer();
|
||||
|
||||
// When changing the variant, the instance is like a new instance.
|
||||
// We call again `onCreated` at the end, like done by the constructor
|
||||
// the first time it's created.
|
||||
// The generated code calls the onCreated super implementation at the end.
|
||||
this.onCreated();
|
||||
}
|
||||
|
||||
@@ -185,34 +172,6 @@ namespace gdjs {
|
||||
newObjectData.animatable || []
|
||||
);
|
||||
}
|
||||
if (oldObjectData.variant !== newObjectData.variant) {
|
||||
const width = this.getWidth();
|
||||
const height = this.getHeight();
|
||||
const hasInnerAreaChanged =
|
||||
oldObjectData.isInnerAreaFollowingParentSize &&
|
||||
this._instanceContainer._initialInnerArea &&
|
||||
this._innerArea &&
|
||||
(this._instanceContainer._initialInnerArea.min[0] !==
|
||||
this._innerArea.min[0] ||
|
||||
this._instanceContainer._initialInnerArea.min[1] !==
|
||||
this._innerArea.min[1] ||
|
||||
this._instanceContainer._initialInnerArea.max[0] !==
|
||||
this._innerArea.max[0] ||
|
||||
this._instanceContainer._initialInnerArea.max[1] !==
|
||||
this._innerArea.max[1]);
|
||||
|
||||
this._reinitializeRenderer();
|
||||
this._initializeFromObjectData(newObjectData);
|
||||
|
||||
// The generated code calls the onCreated super implementation at the end.
|
||||
this.onCreated();
|
||||
|
||||
// Keep the custom size
|
||||
if (hasInnerAreaChanged) {
|
||||
this.setWidth(width);
|
||||
this.setHeight(height);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -8,6 +8,7 @@ namespace gdjs {
|
||||
objectData: ObjectData & CustomObjectConfiguration
|
||||
) {
|
||||
super(parent, objectData);
|
||||
this.getRenderer().reinitialize(this, parent);
|
||||
}
|
||||
|
||||
protected override _createRender(): gdjs.CustomRuntimeObject2DRenderer {
|
||||
|
@@ -24,7 +24,7 @@ namespace gdjs {
|
||||
*
|
||||
* @see gdjs.CustomRuntimeObject._innerArea
|
||||
**/
|
||||
_initialInnerArea: {
|
||||
private _initialInnerArea: {
|
||||
min: [float, float, float];
|
||||
max: [float, float, float];
|
||||
} | null = null;
|
||||
@@ -47,9 +47,6 @@ namespace gdjs {
|
||||
}
|
||||
|
||||
addLayer(layerData: LayerData) {
|
||||
if (this._layers.containsKey(layerData.name)) {
|
||||
return;
|
||||
}
|
||||
const layer = new gdjs.RuntimeCustomObjectLayer(layerData, this);
|
||||
this._layers.put(layerData.name, layer);
|
||||
this._orderedLayers.push(layer);
|
||||
@@ -74,10 +71,6 @@ namespace gdjs {
|
||||
this.onDestroyFromScene(this._parent);
|
||||
}
|
||||
|
||||
const isForcedToOverrideEventsBasedObjectChildrenConfiguration =
|
||||
!eventsBasedObjectVariantData.name &&
|
||||
eventsBasedObjectVariantData.instances.length == 0;
|
||||
|
||||
this._setOriginalInnerArea(eventsBasedObjectVariantData);
|
||||
|
||||
// Registering objects
|
||||
@@ -90,8 +83,7 @@ namespace gdjs {
|
||||
// The children configuration override only applies to the default variant.
|
||||
if (
|
||||
customObjectData.childrenContent &&
|
||||
(!eventsBasedObjectVariantData.name ||
|
||||
isForcedToOverrideEventsBasedObjectChildrenConfiguration)
|
||||
!eventsBasedObjectVariantData.name
|
||||
) {
|
||||
this.registerObject({
|
||||
...childObjectData,
|
||||
|
@@ -811,8 +811,6 @@ namespace gdjs {
|
||||
this._objectsCtor = new Hashtable();
|
||||
this._allInstancesList = [];
|
||||
this._instancesRemoved = [];
|
||||
this._layersCameraCoordinates = {};
|
||||
this._initialBehaviorSharedData = new Hashtable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -222,9 +222,7 @@ namespace gdjs {
|
||||
kind: 'fatal',
|
||||
message:
|
||||
'Unexpected error happened while hot-reloading: ' +
|
||||
error.message +
|
||||
'\n' +
|
||||
error.stack,
|
||||
error.message,
|
||||
});
|
||||
}
|
||||
})
|
||||
@@ -474,24 +472,13 @@ namespace gdjs {
|
||||
newExternalLayoutData.associatedLayout
|
||||
);
|
||||
|
||||
const oldObjectDataList =
|
||||
HotReloader.resolveCustomObjectConfigurations(
|
||||
oldProjectData,
|
||||
oldLayoutData ? oldLayoutData.objects : []
|
||||
);
|
||||
const newObjectDataList =
|
||||
HotReloader.resolveCustomObjectConfigurations(
|
||||
newProjectData,
|
||||
newLayoutData ? newLayoutData.objects : []
|
||||
);
|
||||
|
||||
sceneStack._stack.forEach((runtimeScene) => {
|
||||
this._hotReloadRuntimeSceneInstances(
|
||||
oldProjectData,
|
||||
newProjectData,
|
||||
changedRuntimeBehaviors,
|
||||
oldObjectDataList,
|
||||
newObjectDataList,
|
||||
oldLayoutData ? oldLayoutData.objects : [],
|
||||
newLayoutData ? newLayoutData.objects : [],
|
||||
oldExternalLayoutData.instances,
|
||||
newExternalLayoutData.instances,
|
||||
runtimeScene
|
||||
|
@@ -50,7 +50,12 @@ namespace gdjs {
|
||||
) {
|
||||
this._object = object;
|
||||
this._isContainerDirty = true;
|
||||
this._pixiContainer.removeChildren();
|
||||
const layer = parent.getLayer('');
|
||||
if (layer) {
|
||||
layer
|
||||
.getRenderer()
|
||||
.addRendererObject(this._pixiContainer, object.getZOrder());
|
||||
}
|
||||
}
|
||||
|
||||
getRendererObject() {
|
||||
|
2
GDJS/Runtime/types/project-data.d.ts
vendored
2
GDJS/Runtime/types/project-data.d.ts
vendored
@@ -229,7 +229,7 @@ declare interface EventsBasedObjectVariantData extends InstanceContainerData {
|
||||
/**
|
||||
* A value shared by every object instances.
|
||||
*
|
||||
* @see gdjs.CustomRuntimeObjectInstanceContainer._initialInnerArea
|
||||
* @see gdjs.CustomRuntimeObjectInstanceContainer._originalInnerArea
|
||||
**/
|
||||
_initialInnerArea: {
|
||||
min: [float, float, float];
|
||||
|
@@ -16,7 +16,6 @@ describe('gdjs.CustomRuntimeObject', function () {
|
||||
name: 'MyCustomObject',
|
||||
type: 'MyExtension::MyEventsBasedObject',
|
||||
variant: '',
|
||||
isInnerAreaFollowingParentSize: false,
|
||||
variables: [],
|
||||
behaviors: [],
|
||||
effects: [],
|
||||
|
@@ -100,7 +100,6 @@ describe('gdjs.HotReloader._hotReloadRuntimeGame', () => {
|
||||
effects: [],
|
||||
content: {},
|
||||
childrenContent: {},
|
||||
isInnerAreaFollowingParentSize: false,
|
||||
};
|
||||
|
||||
/** @type {LayerData} */
|
||||
|
@@ -1405,7 +1405,6 @@ interface InitialInstance {
|
||||
double GetCustomDepth();
|
||||
|
||||
[Ref] InitialInstance ResetPersistentUuid();
|
||||
[Const, Ref] DOMString GetPersistentUuid();
|
||||
|
||||
void UpdateCustomProperty(
|
||||
[Const] DOMString name,
|
||||
|
1
GDevelop.js/types.d.ts
vendored
1
GDevelop.js/types.d.ts
vendored
@@ -1167,7 +1167,6 @@ export class InitialInstance extends EmscriptenObject {
|
||||
setCustomDepth(depth: number): void;
|
||||
getCustomDepth(): number;
|
||||
resetPersistentUuid(): InitialInstance;
|
||||
getPersistentUuid(): string;
|
||||
updateCustomProperty(name: string, value: string, globalObjectsContainer: ObjectsContainer, objectsContainer: ObjectsContainer): void;
|
||||
getCustomProperties(globalObjectsContainer: ObjectsContainer, objectsContainer: ObjectsContainer): MapStringPropertyDescriptor;
|
||||
getRawDoubleProperty(name: string): number;
|
||||
|
@@ -44,7 +44,6 @@ declare class gdInitialInstance {
|
||||
setCustomDepth(depth: number): void;
|
||||
getCustomDepth(): number;
|
||||
resetPersistentUuid(): gdInitialInstance;
|
||||
getPersistentUuid(): string;
|
||||
updateCustomProperty(name: string, value: string, globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer): void;
|
||||
getCustomProperties(globalObjectsContainer: gdObjectsContainer, objectsContainer: gdObjectsContainer): gdMapStringPropertyDescriptor;
|
||||
getRawDoubleProperty(name: string): number;
|
||||
|
@@ -30,21 +30,6 @@
|
||||
justify-content: center;
|
||||
|
||||
animation: new-chat-appear 0.5s;
|
||||
|
||||
margin-bottom: var(--safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.aiRequestChatContainer {
|
||||
display: flex;
|
||||
margin-left: 8px;
|
||||
margin-right: 8px;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
justify-content: stretch;
|
||||
flex: 1 1 0%;
|
||||
min-height: 0px;
|
||||
|
||||
margin-bottom: var(--safe-area-inset-bottom);
|
||||
}
|
||||
|
||||
.thinkingText {
|
||||
|
@@ -11,7 +11,7 @@ import { Tooltip } from '@material-ui/core';
|
||||
import Text from '../../UI/Text';
|
||||
import RaisedButton from '../../UI/RaisedButton';
|
||||
import { Trans } from '@lingui/macro';
|
||||
import FlatButtonWithSplitMenu from '../../UI/FlatButtonWithSplitMenu';
|
||||
import RaisedButtonWithSplitMenu from '../../UI/RaisedButtonWithSplitMenu';
|
||||
import Check from '../../UI/CustomSvgIcons/Check';
|
||||
import Error from '../../UI/CustomSvgIcons/Error';
|
||||
import GDevelopThemeContext from '../../UI/Theme/GDevelopThemeContext';
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
type EditorCallbacks,
|
||||
} from '../../EditorFunctions';
|
||||
import Link from '../../UI/Link';
|
||||
import { LineStackLayout, ResponsiveLineStackLayout } from '../../UI/Layout';
|
||||
import { LineStackLayout } from '../../UI/Layout';
|
||||
import ChevronArrowRight from '../../UI/CustomSvgIcons/ChevronArrowRight';
|
||||
import ChevronArrowBottom from '../../UI/CustomSvgIcons/ChevronArrowBottom';
|
||||
import Paper from '../../UI/Paper';
|
||||
@@ -108,7 +108,6 @@ export const FunctionCallRow = React.memo<Props>(function FunctionCallRow({
|
||||
details = result.details;
|
||||
hasDetailsToShow = result.hasDetailsToShow;
|
||||
} catch (error) {
|
||||
console.error('Error rendering function call:', error);
|
||||
text = (
|
||||
<Trans>
|
||||
The editor was unable to display the operation ({functionCall.name})
|
||||
@@ -142,73 +141,60 @@ export const FunctionCallRow = React.memo<Props>(function FunctionCallRow({
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<ResponsiveLineStackLayout
|
||||
justifyContent="space-between"
|
||||
expand
|
||||
noOverflowParent
|
||||
>
|
||||
<LineStackLayout noMargin alignItems="baseline">
|
||||
<Text>{text || 'Working...'}</Text>
|
||||
{hasDetailsToShow && (
|
||||
<Text size="body-small" color="secondary">
|
||||
<Link
|
||||
color="inherit"
|
||||
href={'#'}
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
>
|
||||
<Trans>Details</Trans>
|
||||
{details ? (
|
||||
<ChevronArrowBottom
|
||||
fontSize="small"
|
||||
style={{
|
||||
verticalAlign: 'middle',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ChevronArrowRight
|
||||
fontSize="small"
|
||||
style={{
|
||||
verticalAlign: 'middle',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
</Text>
|
||||
)}
|
||||
</LineStackLayout>
|
||||
<LineStackLayout
|
||||
noMargin
|
||||
alignItems="baseline"
|
||||
justifyContent="flex-end"
|
||||
neverShrink
|
||||
>
|
||||
{!isFinished && !isWorking && (
|
||||
<FlatButtonWithSplitMenu
|
||||
primary
|
||||
style={{ flexShrink: 0 }}
|
||||
onClick={() => onProcessFunctionCalls([functionCall])}
|
||||
label={<Trans>Execute this action</Trans>}
|
||||
buildMenuTemplate={i18n => [
|
||||
{
|
||||
label: i18n._(t`Ignore this`),
|
||||
click: () => {
|
||||
onProcessFunctionCalls([functionCall], {
|
||||
ignore: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{functionCallResultIsErrored && (
|
||||
<RaisedButton
|
||||
color="primary"
|
||||
onClick={() => onProcessFunctionCalls([functionCall])}
|
||||
label={<Trans>Retry</Trans>}
|
||||
/>
|
||||
)}
|
||||
</LineStackLayout>
|
||||
</ResponsiveLineStackLayout>
|
||||
<LineStackLayout noMargin alignItems="baseline">
|
||||
<Text>{text || 'Working...'}</Text>
|
||||
{hasDetailsToShow && (
|
||||
<Text size="body-small" color="secondary">
|
||||
<Link
|
||||
color="inherit"
|
||||
href={'#'}
|
||||
onClick={() => setShowDetails(!showDetails)}
|
||||
>
|
||||
<Trans>Details</Trans>
|
||||
{details ? (
|
||||
<ChevronArrowBottom
|
||||
fontSize="small"
|
||||
style={{
|
||||
verticalAlign: 'middle',
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<ChevronArrowRight
|
||||
fontSize="small"
|
||||
style={{
|
||||
verticalAlign: 'middle',
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Link>
|
||||
</Text>
|
||||
)}
|
||||
</LineStackLayout>
|
||||
{!isFinished && (
|
||||
<RaisedButtonWithSplitMenu
|
||||
primary
|
||||
disabled={isWorking}
|
||||
onClick={() => onProcessFunctionCalls([functionCall])}
|
||||
label={<Trans>Apply</Trans>}
|
||||
buildMenuTemplate={i18n => [
|
||||
{
|
||||
label: i18n._(t`Ignore this`),
|
||||
click: () => {
|
||||
onProcessFunctionCalls([functionCall], {
|
||||
ignore: true,
|
||||
});
|
||||
},
|
||||
},
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
{functionCallResultIsErrored && (
|
||||
<RaisedButton
|
||||
color="primary"
|
||||
onClick={() => onProcessFunctionCalls([functionCall])}
|
||||
label={<Trans>Retry</Trans>}
|
||||
/>
|
||||
)}
|
||||
</LineStackLayout>
|
||||
{details && (
|
||||
<div className={classes.detailsPaperContainer}>
|
||||
|
@@ -37,7 +37,7 @@ import Hammer from '../../UI/CustomSvgIcons/Hammer';
|
||||
import { ChatMessages } from './ChatMessages';
|
||||
import Send from '../../UI/CustomSvgIcons/Send';
|
||||
import { FeedbackBanner } from './FeedbackBanner';
|
||||
import classNames from 'classnames';
|
||||
import LeftLoader from '../../UI/LeftLoader';
|
||||
|
||||
const TOO_MANY_USER_MESSAGES_WARNING_COUNT = 5;
|
||||
const TOO_MANY_USER_MESSAGES_ERROR_COUNT = 10;
|
||||
@@ -79,9 +79,6 @@ type Props = {
|
||||
hasOpenedProject: boolean,
|
||||
isAutoProcessingFunctionCalls: boolean,
|
||||
setAutoProcessFunctionCalls: boolean => void,
|
||||
onStartNewChat: () => void,
|
||||
onRestoreInitialProject?: () => Promise<void>,
|
||||
isCloudProject?: boolean,
|
||||
|
||||
onProcessFunctionCalls: (
|
||||
functionCalls: Array<AiRequestMessageAssistantFunctionCall>,
|
||||
@@ -235,9 +232,6 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
onStartNewAiRequest,
|
||||
onSendMessage,
|
||||
onSendFeedback,
|
||||
onStartNewChat,
|
||||
onRestoreInitialProject,
|
||||
isCloudProject,
|
||||
quota,
|
||||
increaseQuotaOffering,
|
||||
lastSendError,
|
||||
@@ -253,7 +247,6 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
}: Props,
|
||||
ref
|
||||
) => {
|
||||
const [isRestoring, setIsRestoring] = React.useState(false);
|
||||
// TODO: store the default mode in the user preferences?
|
||||
const [newAiRequestMode, setNewAiRequestMode] = React.useState<
|
||||
'chat' | 'agent'
|
||||
@@ -264,7 +257,6 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
setUserRequestTextPerRequestId,
|
||||
] = React.useState<{ [string]: string }>({});
|
||||
const scrollViewRef = React.useRef<ScrollViewInterface | null>(null);
|
||||
const requiredGameId = (aiRequest && aiRequest.gameId) || null;
|
||||
|
||||
const newChatPlaceholder = React.useMemo(
|
||||
() => {
|
||||
@@ -274,7 +266,7 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
? [
|
||||
t`Add solid rocks that falls from the sky at a random position around the player every 0.5 seconds`,
|
||||
t`Add a score and display it on the screen`,
|
||||
t`Create a 3D explosion when the player is hit`,
|
||||
t`Create an explosion when the player is hit`,
|
||||
]
|
||||
: [
|
||||
t`Build a platformer game with a score and coins to collect`,
|
||||
@@ -336,7 +328,6 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
const subscriptionBanner =
|
||||
quota && quota.limitReached && increaseQuotaOffering !== 'none' ? (
|
||||
<GetSubscriptionCard
|
||||
placementId="ai-requests"
|
||||
subscriptionDialogOpeningReason={
|
||||
increaseQuotaOffering === 'subscribe'
|
||||
? 'AI requests (subscribe)'
|
||||
@@ -393,13 +384,7 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
|
||||
if (!aiRequest) {
|
||||
return (
|
||||
<div
|
||||
className={classNames({
|
||||
[classes.newChatContainer]: true,
|
||||
// Move the entire screen up when the soft keyboard is open:
|
||||
'avoid-soft-keyboard': true,
|
||||
})}
|
||||
>
|
||||
<div className={classes.newChatContainer}>
|
||||
<ColumnStackLayout justifyContent="center" expand>
|
||||
<Line noMargin justifyContent="center">
|
||||
<RobotIcon rotating size={40} />
|
||||
@@ -449,8 +434,6 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
maxLength={6000}
|
||||
value={userRequestTextPerAiRequestId[''] || ''}
|
||||
disabled={isSending}
|
||||
hasNeonCorner
|
||||
hasAnimatedNeonCorner={isSending}
|
||||
errored={!!lastSendError}
|
||||
onChange={userRequestText =>
|
||||
setUserRequestTextPerRequestId(
|
||||
@@ -474,32 +457,35 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
alignItems="center"
|
||||
justifyContent="flex-end"
|
||||
>
|
||||
<RaisedButton
|
||||
color="primary"
|
||||
icon={<Send />}
|
||||
label={
|
||||
newAiRequestMode === 'agent' ? (
|
||||
hasOpenedProject ? (
|
||||
<Trans>Build this on my game</Trans>
|
||||
<LeftLoader isLoading={isSending}>
|
||||
<RaisedButton
|
||||
color="primary"
|
||||
icon={<Send />}
|
||||
label={
|
||||
newAiRequestMode === 'agent' ? (
|
||||
hasOpenedProject ? (
|
||||
<Trans>Build this on my game</Trans>
|
||||
) : (
|
||||
<Trans>Start building the game</Trans>
|
||||
)
|
||||
) : (
|
||||
<Trans>Start building the game</Trans>
|
||||
<Trans>Send question</Trans>
|
||||
)
|
||||
) : (
|
||||
<Trans>Send question</Trans>
|
||||
)
|
||||
}
|
||||
style={{ flexShrink: 0 }}
|
||||
disabled={
|
||||
isSending ||
|
||||
!userRequestTextPerAiRequestId[aiRequestId]
|
||||
}
|
||||
onClick={() => {
|
||||
onStartNewAiRequest({
|
||||
mode: newAiRequestMode,
|
||||
userRequest: userRequestTextPerAiRequestId[''],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
}
|
||||
style={{ flexShrink: 0 }}
|
||||
disabled={
|
||||
isSending ||
|
||||
!userRequestTextPerAiRequestId[aiRequestId]
|
||||
}
|
||||
onClick={() => {
|
||||
onStartNewAiRequest({
|
||||
mode: newAiRequestMode,
|
||||
userRequest:
|
||||
userRequestTextPerAiRequestId[''],
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</LeftLoader>
|
||||
</LineStackLayout>
|
||||
</Column>
|
||||
}
|
||||
@@ -531,10 +517,10 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
<Trans>
|
||||
The AI agent will build simple games or features for you.{' '}
|
||||
<Link
|
||||
href={getHelpLink('/interface/ai')}
|
||||
href={getHelpLink('/interface/ask-ai')}
|
||||
color="secondary"
|
||||
onClick={() =>
|
||||
Window.openExternalURL(getHelpLink('/interface/ai'))
|
||||
Window.openExternalURL(getHelpLink('/interface/ask-ai'))
|
||||
}
|
||||
>
|
||||
It can inspect your game objects and events.
|
||||
@@ -546,10 +532,10 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
<Trans>
|
||||
The AI chat is experimental and still being improved.{' '}
|
||||
<Link
|
||||
href={getHelpLink('/interface/ai')}
|
||||
href={getHelpLink('/interface/ask-ai')}
|
||||
color="secondary"
|
||||
onClick={() =>
|
||||
Window.openExternalURL(getHelpLink('/interface/ai'))
|
||||
Window.openExternalURL(getHelpLink('/interface/ask-ai'))
|
||||
}
|
||||
>
|
||||
It has access to your game objects but not events.
|
||||
@@ -586,14 +572,11 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
aiRequest,
|
||||
editorFunctionCallResults,
|
||||
});
|
||||
const isPausedAndHasFunctionCallsToProcess =
|
||||
!isAutoProcessingFunctionCalls && allFunctionCallsToProcess.length > 0;
|
||||
|
||||
const lastMessageIndex = aiRequest.output.length - 1;
|
||||
const lastMessage = aiRequest.output[lastMessageIndex];
|
||||
const shouldDisplayFeedbackBanner =
|
||||
!hasWorkingFunctionCalls &&
|
||||
!isPausedAndHasFunctionCallsToProcess &&
|
||||
!isSending &&
|
||||
aiRequest.status === 'ready' &&
|
||||
aiRequest.mode === 'agent' &&
|
||||
@@ -617,63 +600,14 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
/>
|
||||
);
|
||||
|
||||
const isForAnotherProject =
|
||||
!!requiredGameId &&
|
||||
(!project || requiredGameId !== project.getProjectUuid());
|
||||
const isForAnotherProjectText = isForAnotherProject ? (
|
||||
<Text size="body-small" color="secondary" align="center" noMargin>
|
||||
<Trans>
|
||||
This request is for another project.{' '}
|
||||
<Link href="#" onClick={onStartNewChat}>
|
||||
Start a new chat
|
||||
</Link>{' '}
|
||||
to build on a new project.
|
||||
</Trans>
|
||||
</Text>
|
||||
) : null;
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classNames({
|
||||
[classes.aiRequestChatContainer]: true,
|
||||
})}
|
||||
<Column
|
||||
expand
|
||||
alignItems="stretch"
|
||||
justifyContent="stretch"
|
||||
useFullHeight
|
||||
>
|
||||
<ScrollView ref={scrollViewRef} style={styles.chatScrollView}>
|
||||
{aiRequest &&
|
||||
aiRequest.mode === 'agent' &&
|
||||
aiRequest.initialProjectVersionId &&
|
||||
onRestoreInitialProject &&
|
||||
isCloudProject && (
|
||||
<Paper background="dark" variant="outlined" style={{ marginBottom: 8 }}>
|
||||
<Column>
|
||||
<LineStackLayout
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
>
|
||||
<Text size="body" color="secondary" noMargin>
|
||||
<Trans>Click here to restore the project as it was at the beginning</Trans>
|
||||
</Text>
|
||||
<RaisedButton
|
||||
size="small"
|
||||
color="secondary"
|
||||
label={<Trans>Restore project</Trans>}
|
||||
disabled={isRestoring}
|
||||
onClick={async () => {
|
||||
if (!onRestoreInitialProject) return;
|
||||
setIsRestoring(true);
|
||||
try {
|
||||
await onRestoreInitialProject();
|
||||
} catch (error) {
|
||||
console.error('Error in restore button:', error);
|
||||
} finally {
|
||||
setIsRestoring(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</LineStackLayout>
|
||||
</Column>
|
||||
</Paper>
|
||||
)}
|
||||
<ChatMessages
|
||||
aiRequest={aiRequest}
|
||||
onSendFeedback={onSendFeedback}
|
||||
@@ -710,29 +644,24 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
userMessage: userRequestTextPerAiRequestId[aiRequestId] || '',
|
||||
});
|
||||
}}
|
||||
className={classNames({
|
||||
// Move the form up when the soft keyboard is open:
|
||||
'avoid-soft-keyboard': true,
|
||||
})}
|
||||
>
|
||||
<ColumnStackLayout
|
||||
justifyContent="stretch"
|
||||
alignItems="stretch"
|
||||
noMargin
|
||||
>
|
||||
{aiRequest.mode === 'agent' &&
|
||||
isAutoProcessingFunctionCalls &&
|
||||
{isAutoProcessingFunctionCalls &&
|
||||
(hasWorkingFunctionCalls ||
|
||||
isSending ||
|
||||
aiRequest.status === 'working') ? (
|
||||
<Paper background="dark" variant="outlined">
|
||||
<Paper background="dark" variant="outlined" square>
|
||||
<Column>
|
||||
<LineStackLayout
|
||||
justifyContent="space-between"
|
||||
alignItems="center"
|
||||
>
|
||||
<LineStackLayout alignItems="center" noMargin>
|
||||
<CircularProgress variant="indeterminate" size={12} />
|
||||
<CircularProgress variant="indeterminate" size={10} />
|
||||
<Text size="body" color="secondary" noMargin>
|
||||
<Trans>The AI is building your request.</Trans>
|
||||
</Text>
|
||||
@@ -751,9 +680,9 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
</LineStackLayout>
|
||||
</Column>
|
||||
</Paper>
|
||||
) : aiRequest.mode === 'agent' &&
|
||||
isPausedAndHasFunctionCallsToProcess ? (
|
||||
<Paper background="dark" variant="outlined">
|
||||
) : !isAutoProcessingFunctionCalls &&
|
||||
allFunctionCallsToProcess.length > 0 ? (
|
||||
<Paper background="dark" variant="outlined" square>
|
||||
<Column>
|
||||
<LineStackLayout
|
||||
justifyContent="space-between"
|
||||
@@ -773,7 +702,9 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
onProcessFunctionCalls(allFunctionCallsToProcess);
|
||||
}}
|
||||
>
|
||||
<Trans>Resume all</Trans>
|
||||
<Trans>
|
||||
Apply everything and continue autonomously
|
||||
</Trans>
|
||||
</Link>
|
||||
</Text>
|
||||
</LineStackLayout>
|
||||
@@ -783,10 +714,8 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
<CompactTextAreaFieldWithControls
|
||||
maxLength={6000}
|
||||
value={userRequestTextPerAiRequestId[aiRequestId] || ''}
|
||||
disabled={isSending || isForAnotherProject}
|
||||
disabled={isSending}
|
||||
errored={!!lastSendError}
|
||||
hasNeonCorner
|
||||
hasAnimatedNeonCorner={isSending}
|
||||
onChange={userRequestText =>
|
||||
setUserRequestTextPerRequestId(
|
||||
userRequestTextPerAiRequestId => ({
|
||||
@@ -797,9 +726,7 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
}
|
||||
placeholder={
|
||||
aiRequest.mode === 'agent'
|
||||
? isForAnotherProject
|
||||
? t`You must re-open the project to continue this chat.`
|
||||
: t`Specify something more to the AI to build`
|
||||
? t`Specify something more to the AI to build`
|
||||
: t`Ask a follow up question`
|
||||
}
|
||||
rows={2}
|
||||
@@ -819,7 +746,6 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
disabled={
|
||||
aiRequest.status === 'working' ||
|
||||
isSending ||
|
||||
isForAnotherProject ||
|
||||
!userRequestTextPerAiRequestId[aiRequestId]
|
||||
}
|
||||
icon={<Send />}
|
||||
@@ -841,15 +767,13 @@ export const AiRequestChat = React.forwardRef<Props, AiRequestChatInterface>(
|
||||
alignItems="center"
|
||||
justifyContent="space-between"
|
||||
>
|
||||
{isForAnotherProjectText || errorText || priceText}
|
||||
{errorText || isForAnotherProjectText
|
||||
? null
|
||||
: quotaOrCreditsText}
|
||||
{errorText || priceText}
|
||||
{errorText ? null : quotaOrCreditsText}
|
||||
</LineStackLayout>
|
||||
</Column>
|
||||
</ColumnStackLayout>
|
||||
</form>
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
}
|
||||
);
|
||||
|
@@ -19,8 +19,6 @@ import AuthenticatedUserContext from '../Profile/AuthenticatedUserContext';
|
||||
import { Toolbar } from './Toolbar';
|
||||
import { AskAiHistory } from './AskAiHistory';
|
||||
import { makeSimplifiedProjectBuilder } from '../EditorFunctions/SimplifiedProject/SimplifiedProject';
|
||||
import { type MessageDescriptor } from '../Utils/i18n/MessageDescriptor.flow';
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
canUpgradeSubscription,
|
||||
hasValidSubscriptionPlan,
|
||||
@@ -114,18 +112,15 @@ const useProcessFunctionCalls = ({
|
||||
i18n,
|
||||
project,
|
||||
resourceManagementProps,
|
||||
editorCallbacks,
|
||||
selectedAiRequest,
|
||||
onSendEditorFunctionCallResults,
|
||||
getEditorFunctionCallResults,
|
||||
addEditorFunctionCallResults,
|
||||
onSceneEventsModifiedOutsideEditor,
|
||||
onExtensionInstalled,
|
||||
}: {|
|
||||
i18n: I18nType,
|
||||
project: gdProject | null,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
editorCallbacks: EditorCallbacks,
|
||||
selectedAiRequest: ?AiRequest,
|
||||
onSendEditorFunctionCallResults: () => Promise<void>,
|
||||
getEditorFunctionCallResults: string => Array<EditorFunctionCallResult> | null,
|
||||
@@ -134,7 +129,6 @@ const useProcessFunctionCalls = ({
|
||||
Array<EditorFunctionCallResult>
|
||||
) => void,
|
||||
onSceneEventsModifiedOutsideEditor: (scene: gdLayout) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|}) => {
|
||||
const { ensureExtensionInstalled } = useEnsureExtensionInstalled({
|
||||
project,
|
||||
@@ -143,7 +137,6 @@ const useProcessFunctionCalls = ({
|
||||
const { searchAndInstallAsset } = useSearchAndInstallAsset({
|
||||
project,
|
||||
resourceManagementProps,
|
||||
onExtensionInstalled,
|
||||
});
|
||||
const { generateEvents } = useGenerateEvents({ project });
|
||||
|
||||
@@ -194,7 +187,6 @@ const useProcessFunctionCalls = ({
|
||||
|
||||
const editorFunctionCallResults = await processEditorFunctionCalls({
|
||||
project,
|
||||
editorCallbacks,
|
||||
functionCalls: functionCalls.map(functionCall => ({
|
||||
name: functionCall.name,
|
||||
arguments: functionCall.arguments,
|
||||
@@ -230,7 +222,6 @@ const useProcessFunctionCalls = ({
|
||||
generateEvents,
|
||||
onSceneEventsModifiedOutsideEditor,
|
||||
triggerSendEditorFunctionCallResults,
|
||||
editorCallbacks,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -456,13 +447,6 @@ type Props = {|
|
||||
storageProvider: ?StorageProvider,
|
||||
setToolbar: (?React.Node) => void,
|
||||
i18n: I18nType,
|
||||
onSave?: () => Promise<void>,
|
||||
onOpenCloudProjectOnSpecificVersion?: ({|
|
||||
fileMetadata: FileMetadata,
|
||||
versionId: string,
|
||||
ignoreUnsavedChanges: boolean,
|
||||
openingMessage: MessageDescriptor,
|
||||
|}) => Promise<void>,
|
||||
onCreateEmptyProject: (newProjectSetup: NewProjectSetup) => Promise<void>,
|
||||
onCreateProjectFromExample: (
|
||||
exampleShortHeader: ExampleShortHeader,
|
||||
@@ -470,20 +454,9 @@ type Props = {|
|
||||
i18n: I18nType,
|
||||
isQuickCustomization?: boolean
|
||||
) => Promise<void>,
|
||||
onOpenLayout: (
|
||||
sceneName: string,
|
||||
options: {|
|
||||
openEventsEditor: boolean,
|
||||
openSceneEditor: boolean,
|
||||
focusWhenOpened:
|
||||
| 'scene-or-events-otherwise'
|
||||
| 'scene'
|
||||
| 'events'
|
||||
| 'none',
|
||||
|}
|
||||
) => void,
|
||||
onOpenLayout: (sceneName: string) => void,
|
||||
onOpenEvents: (sceneName: string) => void,
|
||||
onSceneEventsModifiedOutsideEditor: (scene: gdLayout) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|};
|
||||
|
||||
export type AskAiEditorInterface = {|
|
||||
@@ -497,7 +470,6 @@ export type AskAiEditorInterface = {|
|
||||
) => void,
|
||||
onSceneObjectsDeleted: (scene: gdLayout) => void,
|
||||
onSceneEventsModifiedOutsideEditor: (scene: gdLayout) => void,
|
||||
startNewChat: () => void,
|
||||
|};
|
||||
|
||||
export type NewAiRequestOptions = {|
|
||||
@@ -518,22 +490,20 @@ export const AskAiEditor = React.memo<Props>(
|
||||
fileMetadata,
|
||||
storageProvider,
|
||||
i18n,
|
||||
onSave,
|
||||
onOpenCloudProjectOnSpecificVersion,
|
||||
onCreateEmptyProject,
|
||||
onCreateProjectFromExample,
|
||||
onOpenLayout,
|
||||
onOpenEvents,
|
||||
onSceneEventsModifiedOutsideEditor,
|
||||
onExtensionInstalled,
|
||||
}: Props,
|
||||
ref
|
||||
) => {
|
||||
const editorCallbacks: EditorCallbacks = React.useMemo(
|
||||
() => ({
|
||||
onOpenLayout,
|
||||
onSave,
|
||||
onOpenEvents,
|
||||
}),
|
||||
[onOpenLayout, onSave]
|
||||
[onOpenLayout, onOpenEvents]
|
||||
);
|
||||
|
||||
const {
|
||||
@@ -566,34 +536,6 @@ export const AskAiEditor = React.memo<Props>(
|
||||
[setSelectedAiRequestId]
|
||||
);
|
||||
|
||||
const onRestoreInitialProject = React.useCallback(
|
||||
async () => {
|
||||
if (!selectedAiRequest || !selectedAiRequest.initialProjectVersionId || !fileMetadata || !onOpenCloudProjectOnSpecificVersion) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Only restore for cloud projects
|
||||
if (!storageProvider || storageProvider.internalName !== 'Cloud') {
|
||||
console.warn('Project restoration is only available for cloud projects');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await onOpenCloudProjectOnSpecificVersion({
|
||||
fileMetadata,
|
||||
versionId: selectedAiRequest.initialProjectVersionId,
|
||||
ignoreUnsavedChanges: true,
|
||||
openingMessage: i18n._(t`Restoring project to initial state...`),
|
||||
});
|
||||
|
||||
console.info('Project restored to initial version');
|
||||
} catch (error) {
|
||||
console.error('Error restoring project to initial version:', error);
|
||||
}
|
||||
},
|
||||
[selectedAiRequest, fileMetadata, onOpenCloudProjectOnSpecificVersion, storageProvider, i18n]
|
||||
);
|
||||
|
||||
const onOpenHistory = React.useCallback(() => {
|
||||
setIsHistoryOpen(true);
|
||||
}, []);
|
||||
@@ -638,7 +580,6 @@ export const AskAiEditor = React.memo<Props>(
|
||||
onSceneObjectEdited: noop,
|
||||
onSceneObjectsDeleted: noop,
|
||||
onSceneEventsModifiedOutsideEditor: noop,
|
||||
startNewChat: onStartNewChat,
|
||||
}));
|
||||
|
||||
const aiRequestChatRef = React.useRef<AiRequestChatInterface | null>(
|
||||
@@ -735,20 +676,6 @@ export const AskAiEditor = React.memo<Props>(
|
||||
|
||||
// Request is now ready to be started.
|
||||
try {
|
||||
// For agent mode on cloud projects, save the project and store initial version
|
||||
let initialProjectVersionId = null;
|
||||
if (mode === 'agent' && project && onSave && fileMetadata && storageProvider?.internalName === 'Cloud') {
|
||||
try {
|
||||
// Save the project first to create a version
|
||||
await onSave();
|
||||
// Store the current version ID for restoration
|
||||
initialProjectVersionId = fileMetadata.version || null;
|
||||
} catch (error) {
|
||||
console.error('Error saving project before starting AI agent:', error);
|
||||
// Continue anyway, but without initial version storage
|
||||
}
|
||||
}
|
||||
|
||||
const simplifiedProjectBuilder = makeSimplifiedProjectBuilder(gd);
|
||||
const simplifiedProjectJson = project
|
||||
? JSON.stringify(
|
||||
@@ -782,13 +709,7 @@ export const AskAiEditor = React.memo<Props>(
|
||||
|
||||
console.info('Successfully created a new AI request:', aiRequest);
|
||||
setSendingAiRequest(null, false);
|
||||
|
||||
// Add the initial project version to the AI request for local storage
|
||||
const aiRequestWithInitialVersion = {
|
||||
...aiRequest,
|
||||
initialProjectVersionId,
|
||||
};
|
||||
updateAiRequest(aiRequest.id, aiRequestWithInitialVersion);
|
||||
updateAiRequest(aiRequest.id, aiRequest);
|
||||
|
||||
// Select the new AI request just created - unless the user switched to another one
|
||||
// in the meantime.
|
||||
@@ -849,19 +770,6 @@ export const AskAiEditor = React.memo<Props>(
|
||||
]
|
||||
);
|
||||
|
||||
const hasFunctionsCallsToProcess = React.useMemo(
|
||||
() =>
|
||||
selectedAiRequest
|
||||
? getFunctionCallsToProcess({
|
||||
aiRequest: selectedAiRequest,
|
||||
editorFunctionCallResults: getEditorFunctionCallResults(
|
||||
selectedAiRequest.id
|
||||
),
|
||||
}).length > 0
|
||||
: false,
|
||||
[selectedAiRequest, getEditorFunctionCallResults]
|
||||
);
|
||||
|
||||
// Send the results of the function call outputs, if any, and the user message (if any).
|
||||
const onSendMessage = React.useCallback(
|
||||
async ({ userMessage }: {| userMessage: string |}) => {
|
||||
@@ -884,7 +792,6 @@ export const AskAiEditor = React.memo<Props>(
|
||||
// If anything is not finished yet, stop there (we only send all
|
||||
// results at once, AI do not support partial results).
|
||||
if (hasUnfinishedResult) return;
|
||||
if (hasFunctionsCallsToProcess) return;
|
||||
|
||||
// If nothing to send, stop there.
|
||||
if (functionCallOutputs.length === 0 && !userMessage) return;
|
||||
@@ -987,7 +894,6 @@ export const AskAiEditor = React.memo<Props>(
|
||||
setLastSendError,
|
||||
onRefreshLimits,
|
||||
project,
|
||||
hasFunctionsCallsToProcess,
|
||||
]
|
||||
);
|
||||
const onSendEditorFunctionCallResults = React.useCallback(
|
||||
@@ -1033,13 +939,11 @@ export const AskAiEditor = React.memo<Props>(
|
||||
project,
|
||||
resourceManagementProps,
|
||||
selectedAiRequest,
|
||||
editorCallbacks,
|
||||
onSendEditorFunctionCallResults,
|
||||
getEditorFunctionCallResults,
|
||||
addEditorFunctionCallResults,
|
||||
onSceneEventsModifiedOutsideEditor,
|
||||
i18n,
|
||||
onExtensionInstalled,
|
||||
});
|
||||
|
||||
return (
|
||||
@@ -1086,9 +990,6 @@ export const AskAiEditor = React.memo<Props>(
|
||||
}}
|
||||
i18n={i18n}
|
||||
editorCallbacks={editorCallbacks}
|
||||
onStartNewChat={onStartNewChat}
|
||||
onRestoreInitialProject={onRestoreInitialProject}
|
||||
isCloudProject={storageProvider?.internalName === 'Cloud'}
|
||||
/>
|
||||
</div>
|
||||
</Paper>
|
||||
@@ -1130,15 +1031,13 @@ export const renderAskAiEditorContainer = (
|
||||
storageProvider={props.storageProvider}
|
||||
setToolbar={props.setToolbar}
|
||||
isActive={props.isActive}
|
||||
onSave={props.onSave}
|
||||
onOpenCloudProjectOnSpecificVersion={props.onOpenCloudProjectOnSpecificVersion}
|
||||
onCreateEmptyProject={props.onCreateEmptyProject}
|
||||
onCreateProjectFromExample={props.onCreateProjectFromExample}
|
||||
onOpenLayout={props.onOpenLayout}
|
||||
onOpenEvents={props.onOpenEvents}
|
||||
onSceneEventsModifiedOutsideEditor={
|
||||
props.onSceneEventsModifiedOutsideEditor
|
||||
}
|
||||
onExtensionInstalled={props.onExtensionInstalled}
|
||||
/>
|
||||
)}
|
||||
</I18n>
|
||||
|
@@ -12,7 +12,6 @@ import { type ExampleShortHeader } from '../Utils/GDevelopServices/Example';
|
||||
import UrlStorageProvider from '../ProjectsStorage/UrlStorageProvider';
|
||||
import { generateProjectName } from '../ProjectCreation/NewProjectSetupDialog';
|
||||
import { type NewProjectSetup } from '../ProjectCreation/NewProjectSetupDialog';
|
||||
import { Spacer } from '../UI/Grid';
|
||||
|
||||
type RenderCreateAiProjectDialogProps = {
|
||||
onCreateEmptyProject: (newProjectSetup: NewProjectSetup) => Promise<void>,
|
||||
@@ -54,7 +53,6 @@ const CreateAiProjectDialog = ({
|
||||
flexColumnBody
|
||||
>
|
||||
<ColumnStackLayout noMargin>
|
||||
<Spacer />
|
||||
<EmptyAndStartingPointProjects
|
||||
onSelectExampleShortHeader={exampleShortHeader => {
|
||||
onSelectExampleShortHeader(exampleShortHeader);
|
||||
@@ -63,8 +61,6 @@ const CreateAiProjectDialog = ({
|
||||
onSelectEmptyProject();
|
||||
}}
|
||||
/>
|
||||
{/* Use a spacer to avoid extra scrollbars when template tiles are hovered. */}
|
||||
<Spacer />
|
||||
</ColumnStackLayout>
|
||||
</Dialog>
|
||||
);
|
||||
|
@@ -33,7 +33,7 @@ export const useEnsureExtensionInstalled = ({
|
||||
const extensionShortHeader =
|
||||
translatedExtensionShortHeadersByName[extensionName];
|
||||
if (!extensionShortHeader) {
|
||||
throw new Error(`Can't find extension named ${extensionName}.`);
|
||||
throw new Error("Can't find extension with the required name.");
|
||||
}
|
||||
|
||||
await installExtension(
|
||||
|
@@ -16,11 +16,9 @@ import { type ResourceManagementProps } from '../ResourcesList/ResourceSource';
|
||||
export const useSearchAndInstallAsset = ({
|
||||
project,
|
||||
resourceManagementProps,
|
||||
onExtensionInstalled,
|
||||
}: {|
|
||||
project: gdProject | null,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|}) => {
|
||||
const { profile, getAuthorizationHeader } = React.useContext(
|
||||
AuthenticatedUserContext
|
||||
@@ -28,7 +26,6 @@ export const useSearchAndInstallAsset = ({
|
||||
const installAsset = useInstallAsset({
|
||||
project,
|
||||
resourceManagementProps,
|
||||
onExtensionInstalled,
|
||||
});
|
||||
|
||||
return {
|
||||
|
@@ -49,7 +49,6 @@ type Props = {|
|
||||
addedAssetIds: Set<string>,
|
||||
onClose: () => void,
|
||||
onAssetsAdded: (createdObjects: gdObject[]) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
project: gdProject,
|
||||
objectsContainer: ?gdObjectsContainer,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
@@ -62,7 +61,6 @@ const AssetPackInstallDialog = ({
|
||||
addedAssetIds,
|
||||
onClose,
|
||||
onAssetsAdded,
|
||||
onExtensionInstalled,
|
||||
project,
|
||||
objectsContainer,
|
||||
resourceManagementProps,
|
||||
@@ -178,7 +176,6 @@ const AssetPackInstallDialog = ({
|
||||
shouldUpdateExtension: extensionUpdateAction === 'update',
|
||||
eventsFunctionsExtensionsState,
|
||||
project,
|
||||
onExtensionInstalled,
|
||||
});
|
||||
|
||||
// Use a pool to avoid installing an unbounded amount of assets at the same time.
|
||||
@@ -256,7 +253,6 @@ const AssetPackInstallDialog = ({
|
||||
eventsFunctionsExtensionsState,
|
||||
resourceManagementProps,
|
||||
onAssetsAdded,
|
||||
onExtensionInstalled,
|
||||
installPrivateAsset,
|
||||
targetObjectsContainer,
|
||||
targetObjectFolderOrObjectWithContext,
|
||||
|
@@ -29,7 +29,6 @@ type Props = {|
|
||||
onClose: ({ swappingDone: boolean }) => void,
|
||||
// Use minimal UI to hide filters & the details page (useful for Quick Customization)
|
||||
minimalUI?: boolean,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|};
|
||||
|
||||
function AssetSwappingDialog({
|
||||
@@ -41,7 +40,6 @@ function AssetSwappingDialog({
|
||||
resourceManagementProps,
|
||||
onClose,
|
||||
minimalUI,
|
||||
onExtensionInstalled,
|
||||
}: Props) {
|
||||
const shopNavigationState = React.useContext(AssetStoreNavigatorContext);
|
||||
const { openedAssetShortHeader } = shopNavigationState.getCurrentPage();
|
||||
@@ -53,7 +51,6 @@ function AssetSwappingDialog({
|
||||
const installAsset = useInstallAsset({
|
||||
project,
|
||||
resourceManagementProps,
|
||||
onExtensionInstalled,
|
||||
});
|
||||
const { showAlert } = useAlertDialog();
|
||||
|
||||
|
@@ -27,20 +27,13 @@ import ThreeDotsMenu from '../../UI/CustomSvgIcons/ThreeDotsMenu';
|
||||
import useAlertDialog from '../../UI/Alert/useAlertDialog';
|
||||
import ExtensionInstallDialog from '../ExtensionStore/ExtensionInstallDialog';
|
||||
import { getIDEVersion } from '../../Version';
|
||||
import InAppTutorialContext from '../../InAppTutorial/InAppTutorialContext';
|
||||
|
||||
export const useExtensionUpdateAlertDialog = () => {
|
||||
const { showConfirmation } = useAlertDialog();
|
||||
const { currentlyRunningInAppTutorial } = React.useContext(
|
||||
InAppTutorialContext
|
||||
);
|
||||
return async (
|
||||
project: gdProject,
|
||||
behaviorShortHeader: BehaviorShortHeader
|
||||
): Promise<boolean> => {
|
||||
if (currentlyRunningInAppTutorial) {
|
||||
return false;
|
||||
}
|
||||
return await showConfirmation({
|
||||
title: t`Extension update`,
|
||||
message:
|
||||
|
@@ -25,7 +25,7 @@ type Props = {|
|
||||
project: gdProject,
|
||||
onClose: () => void,
|
||||
onInstallExtension: (extensionName: string) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
onCreateNew?: () => void,
|
||||
|};
|
||||
|
||||
@@ -84,7 +84,7 @@ const ExtensionsSearchDialog = ({
|
||||
|
||||
if (installedOrImportedExtensionName) {
|
||||
setExtensionWasInstalled(true);
|
||||
onExtensionInstalled([installedOrImportedExtensionName]);
|
||||
onExtensionInstalled(installedOrImportedExtensionName);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@@ -381,7 +381,6 @@ export type InstallRequiredExtensionsArgs = {|
|
||||
shouldUpdateExtension: boolean,
|
||||
eventsFunctionsExtensionsState: EventsFunctionsExtensionsState,
|
||||
project: gdProject,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|};
|
||||
|
||||
export const installRequiredExtensions = async ({
|
||||
@@ -389,7 +388,6 @@ export const installRequiredExtensions = async ({
|
||||
shouldUpdateExtension,
|
||||
eventsFunctionsExtensionsState,
|
||||
project,
|
||||
onExtensionInstalled,
|
||||
}: InstallRequiredExtensionsArgs): Promise<void> => {
|
||||
const {
|
||||
requiredExtensionShortHeaders,
|
||||
@@ -419,9 +417,6 @@ export const installRequiredExtensions = async ({
|
||||
project,
|
||||
serializedExtensions
|
||||
);
|
||||
onExtensionInstalled(
|
||||
neededExtensions.map(extensionShortHeader => extensionShortHeader.name)
|
||||
);
|
||||
|
||||
const stillMissingExtensions = filterMissingExtensions(
|
||||
gd,
|
||||
|
@@ -1034,7 +1034,6 @@ describe('InstallAsset', () => {
|
||||
shouldUpdateExtension: true,
|
||||
eventsFunctionsExtensionsState: mockEventsFunctionsExtensionsState,
|
||||
project,
|
||||
onExtensionInstalled: () => {},
|
||||
})
|
||||
).rejects.toMatchObject({
|
||||
// It's just because the mock doesn't reloadProjectEventsFunctionsExtensions.
|
||||
@@ -1070,7 +1069,6 @@ describe('InstallAsset', () => {
|
||||
shouldUpdateExtension: true,
|
||||
eventsFunctionsExtensionsState: mockEventsFunctionsExtensionsState,
|
||||
project,
|
||||
onExtensionInstalled: () => {},
|
||||
})
|
||||
).rejects.toMatchObject({
|
||||
message: 'These extensions could not be installed: Flash',
|
||||
@@ -1111,7 +1109,6 @@ describe('InstallAsset', () => {
|
||||
shouldUpdateExtension: true,
|
||||
eventsFunctionsExtensionsState: mockEventsFunctionsExtensionsState,
|
||||
project,
|
||||
onExtensionInstalled: () => {},
|
||||
});
|
||||
|
||||
// No extensions fetched because the extension is already installed.
|
||||
|
@@ -49,15 +49,11 @@ import ErrorBoundary from '../UI/ErrorBoundary';
|
||||
import type { ObjectFolderOrObjectWithContext } from '../ObjectsList/EnumerateObjectFolderOrObject';
|
||||
import LoaderModal from '../UI/LoaderModal';
|
||||
import { AssetStoreNavigatorContext } from './AssetStoreNavigator';
|
||||
import InAppTutorialContext from '../InAppTutorial/InAppTutorialContext';
|
||||
|
||||
const isDev = Window.isDev();
|
||||
|
||||
export const useExtensionUpdateAlertDialog = () => {
|
||||
const { showConfirmation, showDeleteConfirmation } = useAlertDialog();
|
||||
const { currentlyRunningInAppTutorial } = React.useContext(
|
||||
InAppTutorialContext
|
||||
);
|
||||
return async ({
|
||||
project,
|
||||
outOfDateExtensionShortHeaders,
|
||||
@@ -65,9 +61,6 @@ export const useExtensionUpdateAlertDialog = () => {
|
||||
project: gdProject,
|
||||
outOfDateExtensionShortHeaders: Array<ExtensionShortHeader>,
|
||||
|}): Promise<string> => {
|
||||
if (currentlyRunningInAppTutorial) {
|
||||
return 'skip';
|
||||
}
|
||||
const breakingChanges = new Map<
|
||||
ExtensionShortHeader,
|
||||
Array<ExtensionChange>
|
||||
@@ -181,12 +174,10 @@ export const useInstallAsset = ({
|
||||
project,
|
||||
targetObjectFolderOrObjectWithContext,
|
||||
resourceManagementProps,
|
||||
onExtensionInstalled,
|
||||
}: {|
|
||||
project: gdProject | null,
|
||||
targetObjectFolderOrObjectWithContext?: ?ObjectFolderOrObjectWithContext,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|}) => {
|
||||
const shopNavigationState = React.useContext(AssetStoreNavigatorContext);
|
||||
const { openedAssetPack } = shopNavigationState.getCurrentPage();
|
||||
@@ -251,7 +242,6 @@ export const useInstallAsset = ({
|
||||
shouldUpdateExtension: extensionUpdateAction === 'update',
|
||||
eventsFunctionsExtensionsState,
|
||||
project,
|
||||
onExtensionInstalled,
|
||||
});
|
||||
const isPrivate = isPrivateAsset(assetShortHeader);
|
||||
const installOutput = isPrivate
|
||||
@@ -319,7 +309,6 @@ type Props = {|
|
||||
onCreateNewObject: (type: string) => void,
|
||||
onObjectsAddedFromAssets: (Array<gdObject>) => void,
|
||||
targetObjectFolderOrObjectWithContext?: ?ObjectFolderOrObjectWithContext,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|};
|
||||
|
||||
function NewObjectDialog({
|
||||
@@ -332,7 +321,6 @@ function NewObjectDialog({
|
||||
onCreateNewObject,
|
||||
onObjectsAddedFromAssets,
|
||||
targetObjectFolderOrObjectWithContext,
|
||||
onExtensionInstalled,
|
||||
}: Props) {
|
||||
const { isMobile } = useResponsiveWindowSize();
|
||||
const {
|
||||
@@ -390,7 +378,6 @@ function NewObjectDialog({
|
||||
project,
|
||||
resourceManagementProps,
|
||||
targetObjectFolderOrObjectWithContext,
|
||||
onExtensionInstalled,
|
||||
});
|
||||
|
||||
const onInstallAsset = React.useCallback(
|
||||
@@ -449,7 +436,6 @@ function NewObjectDialog({
|
||||
shouldUpdateExtension: extensionUpdateAction === 'update',
|
||||
eventsFunctionsExtensionsState,
|
||||
project,
|
||||
onExtensionInstalled,
|
||||
});
|
||||
|
||||
onCreateNewObject(enumeratedObjectMetadata.name);
|
||||
@@ -471,7 +457,6 @@ function NewObjectDialog({
|
||||
showExtensionUpdateConfirmation,
|
||||
eventsFunctionsExtensionsState,
|
||||
showAlert,
|
||||
onExtensionInstalled,
|
||||
]
|
||||
);
|
||||
|
||||
@@ -686,7 +671,6 @@ function NewObjectDialog({
|
||||
targetObjectFolderOrObjectWithContext={
|
||||
targetObjectFolderOrObjectWithContext
|
||||
}
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
@@ -656,7 +656,6 @@ const PrivateAssetPackInformationPage = ({
|
||||
analyticsMetadata: {
|
||||
reason: 'Claim asset pack',
|
||||
recommendedPlanId: 'gdevelop_gold',
|
||||
placementId: 'claim-asset-pack',
|
||||
},
|
||||
filter: 'individual',
|
||||
})
|
||||
|
@@ -32,7 +32,7 @@ type Props = {|
|
||||
open: boolean,
|
||||
onClose: () => void,
|
||||
onChoose: (type: string, defaultName: string) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
|};
|
||||
|
||||
export default function NewBehaviorDialog({
|
||||
@@ -190,7 +190,7 @@ export default function NewBehaviorDialog({
|
||||
behaviorShortHeader
|
||||
);
|
||||
if (wasExtensionInstalled) {
|
||||
onExtensionInstalled([behaviorShortHeader.extensionName]);
|
||||
onExtensionInstalled(behaviorShortHeader.extensionName);
|
||||
}
|
||||
return wasExtensionInstalled;
|
||||
} finally {
|
||||
|
@@ -325,7 +325,7 @@ export const useManageObjectBehaviors = ({
|
||||
onSizeUpdated?: ?() => void,
|
||||
onBehaviorsUpdated?: ?() => void,
|
||||
onUpdateBehaviorsSharedData: () => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
}): UseManageBehaviorsState => {
|
||||
const [
|
||||
justAddedBehaviorName,
|
||||
@@ -623,7 +623,7 @@ type Props = {|
|
||||
extensionName: string,
|
||||
behaviorName: string
|
||||
) => Promise<void>,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
isListLocked: boolean,
|
||||
|};
|
||||
|
||||
|
@@ -174,7 +174,6 @@ const LockedCourseChapterPreview = React.forwardRef<Props, HTMLDivElement>(
|
||||
analyticsMetadata: {
|
||||
reason: 'Unlock course chapter',
|
||||
recommendedPlanId: 'gdevelop_silver',
|
||||
placementId: 'unlock-course-chapter',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@@ -3,7 +3,6 @@ import { type EventsGenerationResult } from '.';
|
||||
import {
|
||||
editorFunctions,
|
||||
type EditorFunction,
|
||||
type EditorCallbacks,
|
||||
type EditorFunctionCall,
|
||||
type EditorFunctionGenericOutput,
|
||||
type EventsGenerationOptions,
|
||||
@@ -30,7 +29,6 @@ export type EditorFunctionCallResult =
|
||||
export type ProcessEditorFunctionCallsOptions = {|
|
||||
project: gdProject,
|
||||
functionCalls: Array<EditorFunctionCall>,
|
||||
editorCallbacks: EditorCallbacks,
|
||||
ignore: boolean,
|
||||
generateEvents: (
|
||||
options: EventsGenerationOptions
|
||||
@@ -47,7 +45,6 @@ export type ProcessEditorFunctionCallsOptions = {|
|
||||
export const processEditorFunctionCalls = async ({
|
||||
functionCalls,
|
||||
project,
|
||||
editorCallbacks,
|
||||
generateEvents,
|
||||
onSceneEventsModifiedOutsideEditor,
|
||||
ignore,
|
||||
@@ -142,16 +139,6 @@ export const processEditorFunctionCalls = async ({
|
||||
success,
|
||||
output,
|
||||
});
|
||||
|
||||
if (success && args) {
|
||||
if (typeof args.scene_name === 'string') {
|
||||
editorCallbacks.onOpenLayout(args.scene_name, {
|
||||
openEventsEditor: true,
|
||||
openSceneEditor: true,
|
||||
focusWhenOpened: 'none',
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
results.push({
|
||||
status: 'finished',
|
||||
|
@@ -23,7 +23,6 @@ type SimplifiedObject = {|
|
||||
objectType: string,
|
||||
behaviors?: Array<SimplifiedBehavior>,
|
||||
objectVariables?: Array<SimplifiedVariable>,
|
||||
animationNames?: string,
|
||||
|};
|
||||
|
||||
type SimplifiedObjectGroup = {|
|
||||
@@ -166,21 +165,6 @@ export const makeSimplifiedProjectBuilder = (gd: libGDevelop) => {
|
||||
simplifiedObject.objectVariables = objectVariables;
|
||||
}
|
||||
|
||||
const objectConfiguration = object.getConfiguration();
|
||||
const animationNames = mapFor(
|
||||
0,
|
||||
objectConfiguration.getAnimationsCount(),
|
||||
i => {
|
||||
return (
|
||||
objectConfiguration.getAnimationName(i) ||
|
||||
`(animation without name, animation index is: ${i})`
|
||||
);
|
||||
}
|
||||
);
|
||||
if (animationNames.length > 0) {
|
||||
simplifiedObject.animationNames = animationNames.join(', ');
|
||||
}
|
||||
|
||||
return simplifiedObject;
|
||||
};
|
||||
|
||||
|
@@ -400,7 +400,6 @@ describe('SimplifiedProject', () => {
|
||||
"objectType": "Sprite",
|
||||
},
|
||||
Object {
|
||||
"animationNames": "My animation, My other animation, (animation without name, animation index is: 2)",
|
||||
"behaviors": Array [
|
||||
Object {
|
||||
"behaviorName": "Animation",
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -89,7 +89,7 @@ type Props = {|
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
name: string
|
||||
) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
|};
|
||||
|
||||
type State = {|
|
||||
|
@@ -71,7 +71,7 @@ type Props = {|
|
||||
anchorEl?: any, // Unused
|
||||
canPasteInstructions: boolean, // Unused
|
||||
onPasteInstructions: () => void, // Unused
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
|};
|
||||
|
||||
const getInitialStepName = (isNewInstruction: boolean): StepName => {
|
||||
|
@@ -56,7 +56,7 @@ type Props = {|
|
||||
i18n: I18nType,
|
||||
canPasteInstructions: boolean, // Unused
|
||||
onPasteInstructions: () => void, // Unused
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
|};
|
||||
|
||||
/**
|
||||
|
@@ -154,7 +154,7 @@ type Props = {|
|
||||
unsavedChanges?: ?UnsavedChanges,
|
||||
isActive: boolean,
|
||||
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
|};
|
||||
|
||||
type ComponentProps = {|
|
||||
|
@@ -371,7 +371,6 @@ export default class LocalPreviewLauncher extends React.Component<
|
||||
}
|
||||
id="Preview over wifi"
|
||||
title={<Trans>Preview over wifi</Trans>}
|
||||
placementId="preview-wifi"
|
||||
mode="try"
|
||||
isNotShownDuringInAppTutorial
|
||||
/>
|
||||
@@ -383,7 +382,6 @@ export default class LocalPreviewLauncher extends React.Component<
|
||||
title={
|
||||
<Trans>Live preview (apply changes to the running preview)</Trans>
|
||||
}
|
||||
placementId="hot-reloading"
|
||||
mode="try"
|
||||
isNotShownDuringInAppTutorial
|
||||
/>
|
||||
|
@@ -326,7 +326,6 @@ const InviteHome = ({ cloudProjectId }: Props) => {
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Add collaborators on project"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_startup"
|
||||
placementId="invite-collaborators"
|
||||
>
|
||||
<Text>
|
||||
<Trans>
|
||||
|
@@ -350,7 +350,6 @@ function LeaderboardAppearanceDialog({
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Leaderboard customization"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="leaderboards-customization"
|
||||
>
|
||||
<Line>
|
||||
<Column noMargin>
|
||||
@@ -407,7 +406,6 @@ function LeaderboardAppearanceDialog({
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Leaderboard customization"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_startup"
|
||||
placementId="leaderboards-customization"
|
||||
>
|
||||
<Line>
|
||||
<Column noMargin>
|
||||
|
@@ -256,7 +256,6 @@ function LeaderboardOptionsDialog({
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Leaderboard customization"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_startup"
|
||||
placementId="leaderboards-customization"
|
||||
>
|
||||
<Line>
|
||||
<Column noMargin>
|
||||
|
@@ -47,7 +47,6 @@ const MaxLeaderboardCountAlertMessage = () => {
|
||||
<Column expand>
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Leaderboard count per game limit reached"
|
||||
placementId="leaderboards"
|
||||
label={
|
||||
!hasValidSubscription ? (
|
||||
<Trans>Upgrade to GDevelop Premium</Trans>
|
||||
|
@@ -98,7 +98,6 @@ const ServicesWidget = ({
|
||||
analyticsMetadata: {
|
||||
reason: 'Leaderboard count per game limit reached',
|
||||
recommendedPlanId: 'gdevelop_silver',
|
||||
placementId: 'leaderboards',
|
||||
},
|
||||
})
|
||||
}
|
||||
|
@@ -55,18 +55,7 @@ export type RenderEditorContainerProps = {|
|
||||
|
||||
// Opening other editors:
|
||||
onOpenExternalEvents: string => void,
|
||||
onOpenLayout: (
|
||||
sceneName: string,
|
||||
options?: {|
|
||||
openEventsEditor: boolean,
|
||||
openSceneEditor: boolean,
|
||||
focusWhenOpened:
|
||||
| 'scene-or-events-otherwise'
|
||||
| 'scene'
|
||||
| 'events'
|
||||
| 'none',
|
||||
|}
|
||||
) => void,
|
||||
onOpenLayout: string => void,
|
||||
onOpenEvents: (sceneName: string) => void,
|
||||
openInstructionOrExpression: (
|
||||
extension: gdPlatformExtension,
|
||||
@@ -182,7 +171,7 @@ export type RenderEditorContainerProps = {|
|
||||
eventsBasedObjectName: string,
|
||||
variantName: string
|
||||
) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
onDeleteEventsBasedObjectVariant: (
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventBasedObject: gdEventsBasedObject,
|
||||
|
@@ -105,7 +105,6 @@ export class DebuggerEditorContainer extends React.Component<
|
||||
}
|
||||
id="Debugger"
|
||||
title={<Trans>Debugger</Trans>}
|
||||
placementId="debugger"
|
||||
mode="try"
|
||||
/>
|
||||
</React.Fragment>
|
||||
|
@@ -55,7 +55,6 @@ export const MaxProjectCountAlertMessage = ({ margin }: Props) => {
|
||||
}
|
||||
hideButton={!canMaximumCountBeIncreased}
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="max-projects-reached"
|
||||
>
|
||||
<Line>
|
||||
<Column noMargin expand>
|
||||
|
@@ -241,7 +241,6 @@ const EducationMarketingSection = ({
|
||||
analyticsMetadata: {
|
||||
reason: 'Callout in Classroom tab',
|
||||
recommendedPlanId: 'gdevelop_education',
|
||||
placementId: 'education',
|
||||
},
|
||||
});
|
||||
},
|
||||
|
@@ -21,7 +21,6 @@ type Props = {|
|
||||
privateGameTemplateListingData: PrivateGameTemplateListingData
|
||||
) => void,
|
||||
onOpenProfile: () => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|};
|
||||
|
||||
const StoreSection = ({
|
||||
@@ -29,7 +28,6 @@ const StoreSection = ({
|
||||
resourceManagementProps,
|
||||
onOpenPrivateGameTemplateListingData,
|
||||
onOpenProfile,
|
||||
onExtensionInstalled,
|
||||
}: Props) => {
|
||||
const [
|
||||
isAssetPackDialogInstallOpen,
|
||||
@@ -131,7 +129,6 @@ const StoreSection = ({
|
||||
project={project}
|
||||
objectsContainer={null}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
/>
|
||||
)}
|
||||
</SectionContainer>
|
||||
|
@@ -638,7 +638,6 @@ const ManageEducationAccountDialog = ({ onClose }: Props) => {
|
||||
analyticsMetadata: {
|
||||
reason: 'Manage subscription as teacher',
|
||||
recommendedPlanId: 'gdevelop_education',
|
||||
placementId: 'education',
|
||||
},
|
||||
filter: 'education',
|
||||
})
|
||||
|
@@ -152,9 +152,6 @@ type Props = {|
|
||||
templateId?: string
|
||||
) => Promise<void>,
|
||||
|
||||
// Asset store
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|
||||
// Project save
|
||||
onSave: () => Promise<void>,
|
||||
canSave: boolean,
|
||||
@@ -208,7 +205,6 @@ export const HomePage = React.memo<Props>(
|
||||
onOpenTemplateFromCourseChapter,
|
||||
gamesList,
|
||||
gamesPlatformFrameTools,
|
||||
onExtensionInstalled,
|
||||
}: Props,
|
||||
ref
|
||||
) => {
|
||||
@@ -652,7 +648,6 @@ export const HomePage = React.memo<Props>(
|
||||
onOpenPrivateGameTemplateListingData
|
||||
}
|
||||
onOpenProfile={onOpenProfile}
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
/>
|
||||
)}
|
||||
{activeTab === 'team-view' &&
|
||||
@@ -738,6 +733,5 @@ export const renderHomePageContainer = (
|
||||
resourceManagementProps={props.resourceManagementProps}
|
||||
gamesList={props.gamesList}
|
||||
gamesPlatformFrameTools={props.gamesPlatformFrameTools}
|
||||
onExtensionInstalled={props.onExtensionInstalled}
|
||||
/>
|
||||
);
|
||||
|
@@ -485,12 +485,3 @@ export const hasEditorTabOpenedWithKey = (
|
||||
) => {
|
||||
return !!editorTabsState.editors.find(editor => editor.key === key);
|
||||
};
|
||||
|
||||
export const getOpenedAskAiEditor = (
|
||||
state: EditorTabsState
|
||||
): AskAiEditorInterface | null => {
|
||||
const editor = state.editors.find(editor => editor.key === 'ask-ai');
|
||||
|
||||
// $FlowFixMe - the key ensures that the editor is an AskAiEditorInterface.
|
||||
return (editor && editor.editorRef) || null;
|
||||
};
|
||||
|
@@ -263,7 +263,7 @@ export const buildMainMenuDeclarativeTemplate = ({
|
||||
? []
|
||||
: [
|
||||
{
|
||||
label: i18n._(t`Ask AI (AI agent and chatbot)`),
|
||||
label: i18n._(t`Ask AI (GDevelop chatbot)`),
|
||||
onClickSendEvent: 'main-menu-open-ask-ai',
|
||||
},
|
||||
]),
|
||||
|
@@ -49,7 +49,6 @@ import {
|
||||
moveTabToTheRightOfHoveredTab,
|
||||
getCustomObjectEditor,
|
||||
hasEditorTabOpenedWithKey,
|
||||
getOpenedAskAiEditor,
|
||||
} from './EditorTabs/EditorTabsHandler';
|
||||
import { renderDebuggerEditorContainer } from './EditorContainers/DebuggerEditorContainer';
|
||||
import { renderEventsEditorContainer } from './EditorContainers/EventsEditorContainer';
|
||||
@@ -1227,21 +1226,13 @@ const MainFrame = (props: Props) => {
|
||||
|
||||
const openAskAi = React.useCallback(
|
||||
() => {
|
||||
setState(state => {
|
||||
const askAiEditor = getOpenedAskAiEditor(state.editorTabs);
|
||||
if (askAiEditor) {
|
||||
askAiEditor.startNewChat();
|
||||
}
|
||||
|
||||
// Open or focus the AI editor.
|
||||
return {
|
||||
...state,
|
||||
editorTabs: openEditorTab(
|
||||
state.editorTabs,
|
||||
getEditorOpeningOptions({ kind: 'ask-ai', name: '' })
|
||||
),
|
||||
};
|
||||
});
|
||||
setState(state => ({
|
||||
...state,
|
||||
editorTabs: openEditorTab(
|
||||
state.editorTabs,
|
||||
getEditorOpeningOptions({ kind: 'ask-ai', name: '' })
|
||||
),
|
||||
}));
|
||||
},
|
||||
[setState, getEditorOpeningOptions]
|
||||
);
|
||||
@@ -1293,6 +1284,28 @@ const MainFrame = (props: Props) => {
|
||||
toolbar.current.setEditorToolbar(editorToolbar);
|
||||
};
|
||||
|
||||
const onInstallExtension = (extensionName: string) => {
|
||||
const { currentProject } = state;
|
||||
if (!currentProject) return;
|
||||
|
||||
// Close the extension tab before updating/reinstalling the extension.
|
||||
const eventsFunctionsExtensionName = extensionName;
|
||||
|
||||
if (
|
||||
currentProject.hasEventsFunctionsExtensionNamed(
|
||||
eventsFunctionsExtensionName
|
||||
)
|
||||
) {
|
||||
setState(state => ({
|
||||
...state,
|
||||
editorTabs: closeEventsFunctionsExtensionTabs(
|
||||
state.editorTabs,
|
||||
eventsFunctionsExtensionName
|
||||
),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const deleteLayout = (layout: gdLayout) => {
|
||||
const { currentProject } = state;
|
||||
const { i18n } = props;
|
||||
@@ -1399,60 +1412,31 @@ const MainFrame = (props: Props) => {
|
||||
});
|
||||
};
|
||||
|
||||
const onInstallExtension = (extensionName: string) => {
|
||||
const { currentProject } = state;
|
||||
if (!currentProject) return;
|
||||
|
||||
// Close the extension tab before updating/reinstalling the extension.
|
||||
// This is especially important when the extension tab in selected.
|
||||
const eventsFunctionsExtensionName = extensionName;
|
||||
|
||||
if (
|
||||
currentProject.hasEventsFunctionsExtensionNamed(
|
||||
eventsFunctionsExtensionName
|
||||
)
|
||||
) {
|
||||
setState(state => ({
|
||||
...state,
|
||||
editorTabs: closeEventsFunctionsExtensionTabs(
|
||||
state.editorTabs,
|
||||
eventsFunctionsExtensionName
|
||||
),
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
const onExtensionInstalled = (extensionNames: Array<string>) => {
|
||||
const onExtensionInstalled = (extensionName: string) => {
|
||||
const { currentProject } = state;
|
||||
if (!currentProject) {
|
||||
return;
|
||||
}
|
||||
for (const extensionName of extensionNames) {
|
||||
const eventsBasedObjects = currentProject
|
||||
.getEventsFunctionsExtension(extensionName)
|
||||
.getEventsBasedObjects();
|
||||
for (let index = 0; index < eventsBasedObjects.getCount(); index++) {
|
||||
const eventsBasedObject = eventsBasedObjects.getAt(index);
|
||||
gd.EventsBasedObjectVariantHelper.complyVariantsToEventsBasedObject(
|
||||
currentProject,
|
||||
eventsBasedObject
|
||||
);
|
||||
}
|
||||
|
||||
// Close extension tab because `onInstallExtension` is not necessarily
|
||||
// called when the extension tab is not selected.
|
||||
|
||||
// TODO Open the closed tabs back
|
||||
// It would be safer to close the tabs before the extension is installed
|
||||
// but it would make opening them back more complicated.
|
||||
setState(state => ({
|
||||
...state,
|
||||
editorTabs: closeEventsFunctionsExtensionTabs(
|
||||
state.editorTabs,
|
||||
extensionName
|
||||
),
|
||||
}));
|
||||
const eventsBasedObjects = currentProject
|
||||
.getEventsFunctionsExtension(extensionName)
|
||||
.getEventsBasedObjects();
|
||||
for (let index = 0; index < eventsBasedObjects.getCount(); index++) {
|
||||
const eventsBasedObject = eventsBasedObjects.getAt(index);
|
||||
gd.EventsBasedObjectVariantHelper.complyVariantsToEventsBasedObject(
|
||||
currentProject,
|
||||
eventsBasedObject
|
||||
);
|
||||
}
|
||||
// TODO Open the closed tabs back
|
||||
// It would be safer to close the tabs before the extension is installed
|
||||
// but it would make opening them back more complicated.
|
||||
setState(state => ({
|
||||
...state,
|
||||
editorTabs: closeEventsFunctionsExtensionTabs(
|
||||
state.editorTabs,
|
||||
extensionName
|
||||
),
|
||||
}));
|
||||
};
|
||||
|
||||
const renameLayout = (oldName: string, newName: string) => {
|
||||
@@ -1932,32 +1916,16 @@ const MainFrame = (props: Props) => {
|
||||
{
|
||||
openEventsEditor,
|
||||
openSceneEditor,
|
||||
focusWhenOpened,
|
||||
}: {|
|
||||
openEventsEditor: boolean,
|
||||
openSceneEditor: boolean,
|
||||
focusWhenOpened:
|
||||
| 'scene-or-events-otherwise'
|
||||
| 'scene'
|
||||
| 'events'
|
||||
| 'none',
|
||||
|}
|
||||
}: {| openEventsEditor: boolean, openSceneEditor: boolean |}
|
||||
): EditorTabsState => {
|
||||
const sceneEditorOptions = getEditorOpeningOptions({
|
||||
kind: 'layout',
|
||||
name,
|
||||
dontFocusTab: !(
|
||||
focusWhenOpened === 'scene' ||
|
||||
focusWhenOpened === 'scene-or-events-otherwise'
|
||||
),
|
||||
});
|
||||
const eventsEditorOptions = getEditorOpeningOptions({
|
||||
kind: 'layout events',
|
||||
name,
|
||||
dontFocusTab: !(
|
||||
focusWhenOpened === 'events' ||
|
||||
(focusWhenOpened === 'scene-or-events-otherwise' && !openSceneEditor)
|
||||
),
|
||||
dontFocusTab: openSceneEditor,
|
||||
});
|
||||
|
||||
const tabsWithSceneEditor = openSceneEditor
|
||||
@@ -1973,18 +1941,9 @@ const MainFrame = (props: Props) => {
|
||||
const openLayout = React.useCallback(
|
||||
(
|
||||
name: string,
|
||||
options?: {|
|
||||
openEventsEditor: boolean,
|
||||
openSceneEditor: boolean,
|
||||
focusWhenOpened:
|
||||
| 'scene-or-events-otherwise'
|
||||
| 'scene'
|
||||
| 'events'
|
||||
| 'none',
|
||||
|} = {
|
||||
options?: {| openEventsEditor: boolean, openSceneEditor: boolean |} = {
|
||||
openEventsEditor: true,
|
||||
openSceneEditor: true,
|
||||
focusWhenOpened: 'scene',
|
||||
},
|
||||
editorTabs?: EditorTabsState
|
||||
): void => {
|
||||
@@ -1996,7 +1955,6 @@ const MainFrame = (props: Props) => {
|
||||
{
|
||||
openEventsEditor: options.openEventsEditor,
|
||||
openSceneEditor: options.openSceneEditor,
|
||||
focusWhenOpened: options.focusWhenOpened,
|
||||
}
|
||||
),
|
||||
}));
|
||||
@@ -2492,7 +2450,6 @@ const MainFrame = (props: Props) => {
|
||||
{
|
||||
openSceneEditor: true,
|
||||
openEventsEditor: true,
|
||||
focusWhenOpened: 'scene',
|
||||
},
|
||||
editorTabs
|
||||
);
|
||||
@@ -2526,7 +2483,6 @@ const MainFrame = (props: Props) => {
|
||||
{
|
||||
openSceneEditor: true,
|
||||
openEventsEditor: true,
|
||||
focusWhenOpened: 'scene',
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -2557,7 +2513,6 @@ const MainFrame = (props: Props) => {
|
||||
openLayout(firstLayout, {
|
||||
openSceneEditor: true,
|
||||
openEventsEditor: true,
|
||||
focusWhenOpened: 'scene',
|
||||
});
|
||||
|
||||
setIsLoadingProject(false);
|
||||
@@ -4018,17 +3973,20 @@ const MainFrame = (props: Props) => {
|
||||
openLayout(sceneName, {
|
||||
openEventsEditor: true,
|
||||
openSceneEditor: false,
|
||||
focusWhenOpened: 'events',
|
||||
});
|
||||
},
|
||||
onOpenLayout: openLayout,
|
||||
onOpenLayout: (sceneName: string) => {
|
||||
openLayout(sceneName, {
|
||||
openEventsEditor: false,
|
||||
openSceneEditor: true,
|
||||
});
|
||||
},
|
||||
onOpenTemplateFromTutorial: openTemplateFromTutorial,
|
||||
onOpenTemplateFromCourseChapter: openTemplateFromCourseChapter,
|
||||
previewDebuggerServer,
|
||||
hotReloadPreviewButtonProps,
|
||||
resourceManagementProps,
|
||||
onSave: saveProject,
|
||||
onOpenCloudProjectOnSpecificVersion,
|
||||
canSave,
|
||||
onCreateEventsFunction,
|
||||
openInstructionOrExpression,
|
||||
@@ -4437,7 +4395,6 @@ const MainFrame = (props: Props) => {
|
||||
currentProject.getProjectUuid()
|
||||
)}
|
||||
onScreenshotsClaimed={onGameScreenshotsClaimed}
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
/>
|
||||
)}
|
||||
<CustomDragLayer />
|
||||
|
@@ -215,7 +215,7 @@ type Props = {|
|
||||
|
||||
objects: Array<gdObject>,
|
||||
onEditObject: (object: gdObject, initialTab: ?ObjectEditorTab) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
isVariableListLocked: boolean,
|
||||
isBehaviorListLocked: boolean,
|
||||
|};
|
||||
|
@@ -60,7 +60,7 @@ type Props = {|
|
||||
// Preview:
|
||||
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
|
||||
openBehaviorEvents: (extensionName: string, behaviorName: string) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
onOpenEventBasedObjectEditor: (
|
||||
extensionName: string,
|
||||
eventsBasedObjectName: string
|
||||
|
@@ -479,7 +479,6 @@ type Props = {|
|
||||
onObjectPasted?: gdObject => void,
|
||||
getValidatedObjectOrGroupName: (newName: string, global: boolean) => string,
|
||||
onAddObjectInstance: (objectName: string) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|
||||
getThumbnail: (
|
||||
project: gdProject,
|
||||
@@ -519,7 +518,6 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
onObjectPasted,
|
||||
getValidatedObjectOrGroupName,
|
||||
onAddObjectInstance,
|
||||
onExtensionInstalled,
|
||||
|
||||
getThumbnail,
|
||||
unsavedChanges,
|
||||
@@ -1593,7 +1591,6 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
objectsContainer={objectsContainer}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
targetObjectFolderOrObjectWithContext={newObjectDialogOpen.from}
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
/>
|
||||
)}
|
||||
{objectAssetSwappingDialogOpen && (
|
||||
@@ -1609,7 +1606,6 @@ const ObjectsList = React.forwardRef<Props, ObjectsListInterface>(
|
||||
objectsContainer={objectsContainer}
|
||||
object={objectAssetSwappingDialogOpen.objectWithContext.object}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
/>
|
||||
)}
|
||||
</Background>
|
||||
|
@@ -26,32 +26,6 @@ const getVariant = (
|
||||
: eventBasedObject.getDefaultVariant();
|
||||
};
|
||||
|
||||
const getChildObjectConfiguration = (
|
||||
childObjectName: string,
|
||||
eventBasedObject: gdEventsBasedObject,
|
||||
customObjectConfiguration: gdCustomObjectConfiguration,
|
||||
variant: gdEventsBasedObjectVariant
|
||||
): gdObjectConfiguration | null => {
|
||||
// Legacy events-based objects don't have any instance in their default
|
||||
// variant since there wasn't a graphical editor at the time.
|
||||
// In this case, the editor doesn't allow to choose a variant, but a
|
||||
// variant may have stayed after a user rolled back the extension.
|
||||
// This variant must be ignored to match what the editor shows.
|
||||
if (
|
||||
customObjectConfiguration.isForcedToOverrideEventsBasedObjectChildrenConfiguration() ||
|
||||
(variant === eventBasedObject.getDefaultVariant() &&
|
||||
customObjectConfiguration.isMarkedAsOverridingEventsBasedObjectChildrenConfiguration())
|
||||
) {
|
||||
return customObjectConfiguration.getChildObjectConfiguration(
|
||||
childObjectName
|
||||
);
|
||||
}
|
||||
const childObjects = variant.getObjects();
|
||||
return childObjects.hasObjectNamed(childObjectName)
|
||||
? childObjects.getObject(childObjectName).getConfiguration()
|
||||
: null;
|
||||
};
|
||||
|
||||
type PropertyMappingRule = {
|
||||
targetChild: string,
|
||||
targetProperty: string,
|
||||
@@ -215,28 +189,6 @@ export default class RenderedCustomObjectInstance extends Rendered3DInstance
|
||||
};
|
||||
}
|
||||
|
||||
_getChildObjectConfiguration = (
|
||||
childObjectName: string
|
||||
): gdObjectConfiguration | null => {
|
||||
const eventBasedObject = this.eventBasedObject;
|
||||
if (!eventBasedObject) {
|
||||
return null;
|
||||
}
|
||||
const customObjectConfiguration = gd.asCustomObjectConfiguration(
|
||||
this._associatedObjectConfiguration
|
||||
);
|
||||
const variant = getVariant(eventBasedObject, customObjectConfiguration);
|
||||
if (!variant) {
|
||||
return null;
|
||||
}
|
||||
return getChildObjectConfiguration(
|
||||
childObjectName,
|
||||
eventBasedObject,
|
||||
customObjectConfiguration,
|
||||
variant
|
||||
);
|
||||
};
|
||||
|
||||
getRendererOfInstance = (
|
||||
instance: gdInitialInstance
|
||||
): RenderedInstance | Rendered3DInstance => {
|
||||
@@ -247,6 +199,23 @@ export default class RenderedCustomObjectInstance extends Rendered3DInstance
|
||||
const customObjectConfiguration = gd.asCustomObjectConfiguration(
|
||||
this._associatedObjectConfiguration
|
||||
);
|
||||
|
||||
let childObjectConfiguration = null;
|
||||
const variant = this.getVariant();
|
||||
if (variant) {
|
||||
const childObjects = variant.getObjects();
|
||||
if (childObjects.hasObjectNamed(instance.getObjectName())) {
|
||||
const childObject = childObjects.getObject(instance.getObjectName());
|
||||
childObjectConfiguration =
|
||||
this.eventBasedObject &&
|
||||
customObjectConfiguration.isMarkedAsOverridingEventsBasedObjectChildrenConfiguration() &&
|
||||
variant === this.eventBasedObject.getDefaultVariant()
|
||||
? customObjectConfiguration.getChildObjectConfiguration(
|
||||
instance.getObjectName()
|
||||
)
|
||||
: childObject.getConfiguration();
|
||||
}
|
||||
}
|
||||
// Apply property mapping rules on the child instance.
|
||||
const childPropertyOverridings = new Map<string, string>();
|
||||
const customObjectProperties = customObjectConfiguration.getProperties();
|
||||
@@ -269,9 +238,6 @@ export default class RenderedCustomObjectInstance extends Rendered3DInstance
|
||||
}
|
||||
}
|
||||
//...so let's create a renderer.
|
||||
const childObjectConfiguration = this._getChildObjectConfiguration(
|
||||
instance.getObjectName()
|
||||
);
|
||||
renderedInstance = childObjectConfiguration
|
||||
? ObjectsRenderingService.createNewInstanceRenderer(
|
||||
this._project,
|
||||
@@ -396,16 +362,16 @@ export default class RenderedCustomObjectInstance extends Rendered3DInstance
|
||||
const childObjects = variant.getObjects();
|
||||
for (let i = 0; i < childObjects.getObjectsCount(); i++) {
|
||||
const childObject = childObjects.getObjectAt(i);
|
||||
|
||||
const childObjectConfiguration = getChildObjectConfiguration(
|
||||
childObject.getName(),
|
||||
eventBasedObject,
|
||||
customObjectConfiguration,
|
||||
variant
|
||||
);
|
||||
if (!childObjectConfiguration) {
|
||||
continue;
|
||||
}
|
||||
const childObjectConfiguration =
|
||||
customObjectConfiguration.isForcedToOverrideEventsBasedObjectChildrenConfiguration() ||
|
||||
customObjectConfiguration.isMarkedAsOverridingEventsBasedObjectChildrenConfiguration()
|
||||
? customObjectConfiguration.getChildObjectConfiguration(
|
||||
childObject.getName()
|
||||
)
|
||||
: variant
|
||||
.getObjects()
|
||||
.getObject(childObject.getName())
|
||||
.getConfiguration();
|
||||
const childType = childObjectConfiguration.getType();
|
||||
if (
|
||||
childType === 'Sprite' ||
|
||||
|
@@ -173,7 +173,6 @@ const CurrentUsageDisplayer = ({
|
||||
}
|
||||
hideButton={cannotUpgradeSubscription}
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="builds"
|
||||
>
|
||||
<Line>
|
||||
{!isFeatureLocked ? (
|
||||
@@ -220,7 +219,6 @@ const CurrentUsageDisplayer = ({
|
||||
}
|
||||
}
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="builds"
|
||||
>
|
||||
<Line>
|
||||
{!isFeatureLocked ? (
|
||||
|
@@ -3,10 +3,7 @@ import * as React from 'react';
|
||||
import { Trans } from '@lingui/macro';
|
||||
import { Column, Line } from '../../UI/Grid';
|
||||
import { ResponsiveLineStackLayout } from '../../UI/Layout';
|
||||
import {
|
||||
type SubscriptionDialogDisplayReason,
|
||||
type SubscriptionPlacementId,
|
||||
} from '../../Utils/Analytics/EventSender';
|
||||
import { type SubscriptionDialogDisplayReason } from '../../Utils/Analytics/EventSender';
|
||||
import { SubscriptionSuggestionContext } from './SubscriptionSuggestionContext';
|
||||
import RaisedButton from '../../UI/RaisedButton';
|
||||
import FlatButton from '../../UI/FlatButton';
|
||||
@@ -56,7 +53,6 @@ type Props = {|
|
||||
| 'gdevelop_startup'
|
||||
| 'gdevelop_education',
|
||||
canHide?: boolean,
|
||||
placementId: SubscriptionPlacementId,
|
||||
|};
|
||||
|
||||
const GetSubscriptionCard = ({
|
||||
@@ -70,7 +66,6 @@ const GetSubscriptionCard = ({
|
||||
filter,
|
||||
recommendedPlanIdIfNoSubscription,
|
||||
canHide,
|
||||
placementId,
|
||||
}: Props) => {
|
||||
const [isHidden, setIsHidden] = React.useState(false);
|
||||
const { subscription } = React.useContext(AuthenticatedUserContext);
|
||||
@@ -121,7 +116,6 @@ const GetSubscriptionCard = ({
|
||||
analyticsMetadata: {
|
||||
reason: subscriptionDialogOpeningReason,
|
||||
recommendedPlanId: actualPlanIdToRecommend,
|
||||
placementId,
|
||||
},
|
||||
filter,
|
||||
});
|
||||
|
@@ -16,7 +16,6 @@ import { isNativeMobileApp } from '../../Utils/Platform';
|
||||
import InAppTutorialContext from '../../InAppTutorial/InAppTutorialContext';
|
||||
import GetSubscriptionCard from './GetSubscriptionCard';
|
||||
import { ColumnStackLayout } from '../../UI/Layout';
|
||||
import { type SubscriptionPlacementId } from '../../Utils/Analytics/EventSender';
|
||||
|
||||
export type SubscriptionCheckerInterface = {|
|
||||
checkUserHasSubscription: () => boolean,
|
||||
@@ -30,7 +29,6 @@ type Props = {|
|
||||
| 'Debugger'
|
||||
| 'Hot reloading'
|
||||
| 'Preview over wifi',
|
||||
placementId: SubscriptionPlacementId,
|
||||
onChangeSubscription?: () => Promise<void> | void,
|
||||
mode: 'try' | 'mandatory',
|
||||
isNotShownDuringInAppTutorial?: boolean,
|
||||
@@ -41,14 +39,7 @@ const SubscriptionChecker = React.forwardRef<
|
||||
SubscriptionCheckerInterface
|
||||
>(
|
||||
(
|
||||
{
|
||||
mode,
|
||||
id,
|
||||
title,
|
||||
onChangeSubscription,
|
||||
placementId,
|
||||
isNotShownDuringInAppTutorial,
|
||||
},
|
||||
{ mode, id, title, onChangeSubscription, isNotShownDuringInAppTutorial },
|
||||
ref
|
||||
) => {
|
||||
const authenticatedUser = React.useContext(AuthenticatedUserContext);
|
||||
@@ -131,7 +122,6 @@ const SubscriptionChecker = React.forwardRef<
|
||||
setDialogOpen(false);
|
||||
}}
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId={placementId}
|
||||
>
|
||||
<Column noMargin expand>
|
||||
<Text>
|
||||
|
@@ -341,10 +341,7 @@ const SubscriptionDetails = ({
|
||||
primary
|
||||
onClick={() => {
|
||||
openSubscriptionDialog({
|
||||
analyticsMetadata: {
|
||||
reason: 'Consult profile',
|
||||
placementId: 'profile',
|
||||
},
|
||||
analyticsMetadata: { reason: 'Consult profile' },
|
||||
});
|
||||
}}
|
||||
disabled={isManageSubscriptionLoading}
|
||||
@@ -393,7 +390,6 @@ const SubscriptionDetails = ({
|
||||
<GetSubscriptionCard
|
||||
label={<Trans>Choose a subscription</Trans>}
|
||||
subscriptionDialogOpeningReason="Consult profile"
|
||||
placementId="profile"
|
||||
>
|
||||
<Text noMargin>
|
||||
<Trans>
|
||||
@@ -444,10 +440,7 @@ const SubscriptionDetails = ({
|
||||
color={buttonColor}
|
||||
onClick={() =>
|
||||
openSubscriptionDialog({
|
||||
analyticsMetadata: {
|
||||
reason: 'Consult profile',
|
||||
placementId: 'profile',
|
||||
},
|
||||
analyticsMetadata: { reason: 'Consult profile' },
|
||||
filter: key,
|
||||
})
|
||||
}
|
||||
@@ -465,7 +458,6 @@ const SubscriptionDetails = ({
|
||||
label={<Trans>Choose a subscription</Trans>}
|
||||
subscriptionDialogOpeningReason="Consult profile"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="profile"
|
||||
>
|
||||
<Text noMargin>
|
||||
<Trans>
|
||||
|
@@ -5,7 +5,6 @@ import SubscriptionDialog from './SubscriptionDialog';
|
||||
import {
|
||||
sendSubscriptionDialogShown,
|
||||
type SubscriptionDialogDisplayReason,
|
||||
type SubscriptionPlacementId,
|
||||
} from '../../Utils/Analytics/EventSender';
|
||||
import { isNativeMobileApp } from '../../Utils/Platform';
|
||||
import {
|
||||
@@ -27,7 +26,6 @@ export type SubscriptionType = 'individual' | 'team' | 'education';
|
||||
export type SubscriptionAnalyticsMetadata = {|
|
||||
reason: SubscriptionDialogDisplayReason,
|
||||
recommendedPlanId?: string,
|
||||
placementId: SubscriptionPlacementId,
|
||||
preStep?: 'subscriptionChecker',
|
||||
|};
|
||||
|
||||
|
@@ -238,7 +238,6 @@ export const LoadingScreenEditor = ({
|
||||
<GetSubscriptionCard
|
||||
subscriptionDialogOpeningReason="Disable GDevelop splash at startup"
|
||||
recommendedPlanIdIfNoSubscription="gdevelop_silver"
|
||||
placementId="gdevelop-branding"
|
||||
>
|
||||
<Text>
|
||||
<Trans>
|
||||
@@ -556,7 +555,6 @@ export const LoadingScreenEditor = ({
|
||||
mode="mandatory"
|
||||
id="Disable GDevelop splash at startup"
|
||||
title={<Trans>Disable GDevelop splash at startup</Trans>}
|
||||
placementId="gdevelop-branding"
|
||||
/>
|
||||
</ColumnStackLayout>
|
||||
)}
|
||||
|
@@ -29,11 +29,6 @@ export type SceneTreeViewItemCallbacks = {|
|
||||
options?: {|
|
||||
openEventsEditor: boolean,
|
||||
openSceneEditor: boolean,
|
||||
focusWhenOpened:
|
||||
| 'scene-or-events-otherwise'
|
||||
| 'scene'
|
||||
| 'events'
|
||||
| 'none',
|
||||
|}
|
||||
) => void,
|
||||
|};
|
||||
@@ -96,11 +91,7 @@ export class SceneTreeViewItemContent implements TreeViewItemContent {
|
||||
}
|
||||
|
||||
onClick(): void {
|
||||
this.props.onOpenLayout(this.scene.getName(), {
|
||||
openEventsEditor: true,
|
||||
openSceneEditor: true,
|
||||
focusWhenOpened: 'scene',
|
||||
});
|
||||
this.props.onOpenLayout(this.scene.getName());
|
||||
}
|
||||
|
||||
rename(newName: string): void {
|
||||
@@ -124,7 +115,6 @@ export class SceneTreeViewItemContent implements TreeViewItemContent {
|
||||
this.props.onOpenLayout(this.scene.getName(), {
|
||||
openSceneEditor: true,
|
||||
openEventsEditor: false,
|
||||
focusWhenOpened: 'scene',
|
||||
}),
|
||||
},
|
||||
{
|
||||
@@ -134,7 +124,6 @@ export class SceneTreeViewItemContent implements TreeViewItemContent {
|
||||
this.props.onOpenLayout(this.scene.getName(), {
|
||||
openSceneEditor: false,
|
||||
openEventsEditor: true,
|
||||
focusWhenOpened: 'events',
|
||||
}),
|
||||
},
|
||||
{
|
||||
|
@@ -422,7 +422,7 @@ type Props = {|
|
||||
onShareProject: () => void,
|
||||
onOpenHomePage: () => void,
|
||||
toggleProjectManager: () => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
|
||||
// Main menu
|
||||
mainMenuCallbacks: MainMenuCallbacks,
|
||||
|
@@ -31,7 +31,6 @@ type Props = {|
|
||||
sourceGameId: string,
|
||||
gameScreenshotUrls: Array<string>,
|
||||
onScreenshotsClaimed: () => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|};
|
||||
|
||||
export const QuickCustomizationDialog = ({
|
||||
@@ -47,7 +46,6 @@ export const QuickCustomizationDialog = ({
|
||||
sourceGameId,
|
||||
gameScreenshotUrls,
|
||||
onScreenshotsClaimed,
|
||||
onExtensionInstalled,
|
||||
}: Props) => {
|
||||
const { triggerUnsavedChanges } = React.useContext(UnsavedChangesContext);
|
||||
const gameAndBuildsManager = useGameAndBuildsManager({
|
||||
@@ -77,7 +75,6 @@ export const QuickCustomizationDialog = ({
|
||||
onContinueQuickCustomization,
|
||||
gameScreenshotUrls,
|
||||
onScreenshotsClaimed,
|
||||
onExtensionInstalled,
|
||||
});
|
||||
|
||||
const name = project.getName();
|
||||
|
@@ -14,7 +14,6 @@ import TipCard from './TipCard';
|
||||
type Props = {|
|
||||
project: gdProject,
|
||||
resourceManagementProps: ResourceManagementProps,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|};
|
||||
|
||||
const styles = {
|
||||
@@ -30,7 +29,6 @@ const styles = {
|
||||
export const QuickObjectReplacer = ({
|
||||
project,
|
||||
resourceManagementProps,
|
||||
onExtensionInstalled,
|
||||
}: Props) => {
|
||||
const [selectedObjectToSwap, setSelectedObjectToSwap] = React.useState(null);
|
||||
|
||||
@@ -98,7 +96,6 @@ export const QuickObjectReplacer = ({
|
||||
setSelectedObjectToSwap(null);
|
||||
}}
|
||||
minimalUI
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
/>
|
||||
)}
|
||||
</ColumnStackLayout>
|
||||
|
@@ -149,7 +149,6 @@ type Props = {|
|
||||
onContinueQuickCustomization: () => void,
|
||||
gameScreenshotUrls: Array<string>,
|
||||
onScreenshotsClaimed: () => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
|};
|
||||
|
||||
export const renderQuickCustomization = ({
|
||||
@@ -166,7 +165,6 @@ export const renderQuickCustomization = ({
|
||||
onContinueQuickCustomization,
|
||||
gameScreenshotUrls,
|
||||
onScreenshotsClaimed,
|
||||
onExtensionInstalled,
|
||||
}: Props) => {
|
||||
return {
|
||||
title: quickCustomizationState.step.title,
|
||||
@@ -176,7 +174,6 @@ export const renderQuickCustomization = ({
|
||||
<QuickObjectReplacer
|
||||
project={project}
|
||||
resourceManagementProps={resourceManagementProps}
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
/>
|
||||
) : quickCustomizationState.step.name === 'tweak-behaviors' ? (
|
||||
<QuickBehaviorsTweaker
|
||||
|
@@ -98,7 +98,7 @@ export type SceneEditorsDisplayProps = {|
|
||||
i18n: I18nType,
|
||||
objectOrGroupName: string
|
||||
) => boolean,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
|
||||
updateBehaviorsSharedData: () => void,
|
||||
onInstancesAdded: (Array<gdInitialInstance>) => void,
|
||||
|
@@ -42,7 +42,7 @@ type Props = {|
|
||||
objects: Array<gdObject>,
|
||||
onEditObject: (object: gdObject, initialTab: ?ObjectEditorTab) => void,
|
||||
onUpdateBehaviorsSharedData: () => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
isBehaviorListLocked: boolean,
|
||||
|
||||
// For instances:
|
||||
|
@@ -96,7 +96,6 @@ const MosaicEditorsDisplay = React.forwardRef<
|
||||
initialInstances,
|
||||
selectedLayer,
|
||||
onSelectInstances,
|
||||
onExtensionInstalled,
|
||||
} = props;
|
||||
const { isMobile } = useResponsiveWindowSize();
|
||||
const {
|
||||
@@ -428,7 +427,6 @@ const MosaicEditorsDisplay = React.forwardRef<
|
||||
unsavedChanges={props.unsavedChanges}
|
||||
hotReloadPreviewButtonProps={props.hotReloadPreviewButtonProps}
|
||||
isListLocked={isCustomVariant}
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
/>
|
||||
)}
|
||||
</I18n>
|
||||
|
@@ -72,7 +72,6 @@ const SwipeableDrawerEditorsDisplay = React.forwardRef<
|
||||
initialInstances,
|
||||
selectedLayer,
|
||||
onSelectInstances,
|
||||
onExtensionInstalled,
|
||||
} = props;
|
||||
const selectedInstances = props.instancesSelection.getSelectedInstances();
|
||||
const { values } = React.useContext(PreferencesContext);
|
||||
@@ -374,7 +373,6 @@ const SwipeableDrawerEditorsDisplay = React.forwardRef<
|
||||
props.hotReloadPreviewButtonProps
|
||||
}
|
||||
isListLocked={isCustomVariant}
|
||||
onExtensionInstalled={onExtensionInstalled}
|
||||
/>
|
||||
)}
|
||||
</I18n>
|
||||
|
@@ -142,7 +142,7 @@ type Props = {|
|
||||
eventsBasedObjectName: string,
|
||||
variantName: string
|
||||
) => void,
|
||||
onExtensionInstalled: (extensionNames: Array<string>) => void,
|
||||
onExtensionInstalled: (extensionName: string) => void,
|
||||
onDeleteEventsBasedObjectVariant: (
|
||||
eventsFunctionsExtension: gdEventsFunctionsExtension,
|
||||
eventBasedObject: gdEventsBasedObject,
|
||||
|
@@ -49,8 +49,7 @@
|
||||
.container:not(.disabled):not(.errored):hover .compactTextAreaField::before {
|
||||
border-bottom: 1px solid var(--theme-text-field-hover-border-color);
|
||||
}
|
||||
.container:not(.disabled):not(.errored):focus-within
|
||||
.compactTextAreaField::before {
|
||||
.container:not(.disabled):not(.errored):focus-within .compactTextAreaField::before {
|
||||
border-bottom: 1px solid var(--theme-text-field-active-border-color);
|
||||
}
|
||||
|
||||
@@ -91,74 +90,3 @@
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Animated corner neon */
|
||||
.neonCorner {
|
||||
background: var(--theme-text-field-default-background-color);
|
||||
border-radius: 4px;
|
||||
padding: 2px;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.neonCorner textarea {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
.neonCorner div {
|
||||
position: relative;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.neonCorner::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: -100px;
|
||||
width: calc(100% + 200px);
|
||||
background: conic-gradient(
|
||||
from 90deg,
|
||||
transparent,
|
||||
transparent,
|
||||
transparent,
|
||||
transparent,
|
||||
transparent,
|
||||
transparent,
|
||||
#b07bf7,
|
||||
#d2b5ff,
|
||||
transparent,
|
||||
transparent,
|
||||
transparent,
|
||||
transparent
|
||||
);
|
||||
rotate: 0deg;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.animatedNeonCorner::before {
|
||||
animation: rotateNeon 3s linear infinite;
|
||||
}
|
||||
|
||||
.container.errored:not(.disabled) .neonCorner::before {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.neonCorner::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 1px;
|
||||
left: 1px;
|
||||
right: 1px;
|
||||
bottom: 1px;
|
||||
background: var(--theme-text-field-default-background-color);
|
||||
border-radius: 3px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
@keyframes rotateNeon {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
@@ -19,8 +19,6 @@ export type CompactTextAreaFieldWithControlsProps = {|
|
||||
rows?: number,
|
||||
maxLength?: number,
|
||||
controls: React.Node,
|
||||
hasNeonCorner?: boolean,
|
||||
hasAnimatedNeonCorner?: boolean,
|
||||
|};
|
||||
|
||||
export const CompactTextAreaFieldWithControls = ({
|
||||
@@ -34,8 +32,6 @@ export const CompactTextAreaFieldWithControls = ({
|
||||
maxLength,
|
||||
onSubmit,
|
||||
controls,
|
||||
hasNeonCorner,
|
||||
hasAnimatedNeonCorner,
|
||||
}: CompactTextAreaFieldWithControlsProps) => {
|
||||
const idToUse = React.useRef<string>(id || makeTimestampedId());
|
||||
|
||||
@@ -52,9 +48,6 @@ export const CompactTextAreaFieldWithControls = ({
|
||||
<div
|
||||
className={classNames({
|
||||
[classes.compactTextAreaField]: true,
|
||||
[classes.neonCorner]: hasNeonCorner,
|
||||
[classes.animatedNeonCorner]:
|
||||
hasNeonCorner && hasAnimatedNeonCorner,
|
||||
})}
|
||||
>
|
||||
<textarea
|
||||
|
@@ -94,7 +94,6 @@ type LineStackLayoutProps = {|
|
||||
children: React.Node,
|
||||
useLargeSpacer?: boolean,
|
||||
overflow?: 'hidden', // allows children Text components to use text ellipsis when they are too long
|
||||
neverShrink?: boolean,
|
||||
|};
|
||||
|
||||
export const LineStackLayout = ({
|
||||
@@ -106,7 +105,6 @@ export const LineStackLayout = ({
|
||||
children,
|
||||
useLargeSpacer,
|
||||
overflow,
|
||||
neverShrink,
|
||||
}: LineStackLayoutProps) => {
|
||||
let isFirstChild = true;
|
||||
return (
|
||||
@@ -117,7 +115,6 @@ export const LineStackLayout = ({
|
||||
expand={expand}
|
||||
noMargin={noMargin}
|
||||
overflow={overflow}
|
||||
neverShrink={neverShrink}
|
||||
>
|
||||
{React.Children.map(children, (child, index) => {
|
||||
if (!child) return null;
|
||||
|
@@ -28,11 +28,6 @@ const styles = {
|
||||
|
||||
const ESTIMATED_ROW_HEIGHT = 90;
|
||||
|
||||
// Keep overscanCount relatively high so that:
|
||||
// - during in-app tutorials we make sure the tooltip displayer finds
|
||||
// the elements to highlight
|
||||
const OVERSCAN_CELLS_COUNT = 25;
|
||||
|
||||
/** A virtualized list of search results, caching the searched item heights. */
|
||||
export const ListSearchResults = <SearchItem>({
|
||||
disableAutoTranslate,
|
||||
@@ -150,24 +145,6 @@ export const ListSearchResults = <SearchItem>({
|
||||
rowCount={searchItems.length}
|
||||
cellRenderer={renderRow}
|
||||
style={styles.grid}
|
||||
// We override this function to avoid a bug in react-virtualized
|
||||
// where the overscanCellsCount is not taken into account after a scroll
|
||||
// see https://github.com/bvaughn/react-virtualized/issues/1582#issuecomment-785073746
|
||||
overscanIndicesGetter={({
|
||||
cellCount,
|
||||
overscanCellsCount,
|
||||
startIndex,
|
||||
stopIndex,
|
||||
}) => ({
|
||||
overscanStartIndex: Math.max(
|
||||
0,
|
||||
startIndex - OVERSCAN_CELLS_COUNT
|
||||
),
|
||||
overscanStopIndex: Math.min(
|
||||
cellCount - 1,
|
||||
stopIndex + OVERSCAN_CELLS_COUNT
|
||||
),
|
||||
})}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
|
@@ -526,7 +526,7 @@
|
||||
},
|
||||
"default": {
|
||||
"background-color": {
|
||||
"value": "#434343"
|
||||
"value": "rgba(255, 255, 255, 0.09)"
|
||||
},
|
||||
"error": {
|
||||
"value": "#FF8569",
|
||||
|
@@ -517,7 +517,7 @@
|
||||
},
|
||||
"default": {
|
||||
"background-color": {
|
||||
"value": "#4d5362"
|
||||
"value": "rgba(255, 255, 255, 0.09)"
|
||||
},
|
||||
"error": {
|
||||
"value": "#FF8569",
|
||||
|
@@ -522,7 +522,7 @@
|
||||
},
|
||||
"default": {
|
||||
"background-color": {
|
||||
"value": "#3b3f46"
|
||||
"value": "rgba(255, 255, 255, 0.09)"
|
||||
},
|
||||
"error": {
|
||||
"value": "#FF8569",
|
||||
|
@@ -518,7 +518,7 @@
|
||||
},
|
||||
"default": {
|
||||
"background-color": {
|
||||
"value": "#333141"
|
||||
"value": "rgba(255, 255, 255, 0.09)"
|
||||
},
|
||||
"error": {
|
||||
"value": "#FF8569",
|
||||
|
@@ -519,7 +519,7 @@
|
||||
},
|
||||
"default": {
|
||||
"background-color": {
|
||||
"value": "#1b3e49"
|
||||
"value": "rgba(255, 255, 255, 0.09)"
|
||||
},
|
||||
"error": {
|
||||
"value": "#FF8569",
|
||||
|
@@ -451,7 +451,6 @@ const TreeViewRow = <Item: ItemBaseAttributes>(props: Props<Item>) => {
|
||||
{rightButton &&
|
||||
(rightButton.primary ? (
|
||||
<TreeViewRightPrimaryButton
|
||||
id={rightButton.id}
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
if (rightButton.click) {
|
||||
|
@@ -742,7 +742,7 @@ const TreeView = <Item: ItemBaseAttributes>(
|
||||
// the elements to highlight
|
||||
// - on mobile it avoids jumping screens. This can happen when an item
|
||||
// name is edited, the keyboard opens and reduces the window height
|
||||
// making the item disappear (because of virtualization).
|
||||
// making the item disappear (because or virtualization).
|
||||
overscanCount={20}
|
||||
>
|
||||
{TreeViewRow}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user