Compare commits

..

61 Commits

Author SHA1 Message Date
Florian Rival
9d7ddab67b Remove TODO [skip ci] [ci skip] 2025-06-17 15:31:46 +02:00
Florian Rival
3c327c7193 Merge branch 'master' into experimental-build/agent 2025-06-17 14:05:15 +02:00
Florian Rival
34e71908a1 Add loader when launching an AI request 2025-06-17 14:03:39 +02:00
Florian Rival
62485db64e Add dialog to choose starter game for AI 2025-06-16 18:50:38 +02:00
Florian Rival
af8d0f2a52 Merge branch 'master' into feat/agent 2025-06-15 20:53:48 +02:00
Florian Rival
f6aab10d22 Add feedback banner for AI agent 2025-06-15 18:13:53 +02:00
Florian Rival
69d6723f1c Add analytics for ai requests 2025-06-15 15:47:44 +02:00
Florian Rival
704b157285 Add link to AI agent in new project dialog 2025-06-15 11:59:35 +02:00
Florian Rival
f50ddb6872 Add AI generated event id to results of events generation 2025-06-13 18:13:17 +02:00
Florian Rival
eb7d4662bc Fix TopDown behavior default controls boolean 2025-06-13 17:15:42 +02:00
Florian Rival
1ec1a1e600 Add information about hidden instructions to extension summaries 2025-06-13 16:53:04 +02:00
Florian Rival
ec16740520 Add descriptions of instances to simplified project 2025-06-13 12:41:09 +02:00
Florian Rival
e26e239b1c Merge branch 'master' into feat/commands 2025-06-12 15:37:52 +02:00
Florian Rival
0e48539a99 Adapt styling of AI chat 2025-06-12 13:42:49 +02:00
Florian Rival
d8a8e759a0 Add support for project specific extension summaries for AI requests 2025-06-11 01:31:09 +02:00
Florian Rival
36c03a054e Merge branch 'master' into feat/commands 2025-06-09 14:44:09 +02:00
Florian Rival
313d60a315 Improve Ask AI UI and fix platformer behavior default controls boolean 2025-06-08 01:10:42 +02:00
Florian Rival
5dd2f85796 Update wording 2025-06-06 00:45:28 +02:00
Florian Rival
7e844ae539 Fix crash when switching to events editor after changes outside of it and allow undo/redo AI changes in events 2025-06-05 16:59:16 +02:00
Florian Rival
78da8185e5 Ensure behavior shared data are updated when a new behavior is added from an editor function call 2025-06-05 12:44:08 +02:00
Florian Rival
6de8b5503d Make property change non case sensitive in editor function calls 2025-06-04 16:11:36 +02:00
Florian Rival
ea4c9ff3fa Merge branch 'master' into feat/commands 2025-06-04 00:21:09 +02:00
Florian Rival
2762415729 Add support for AI editing array variables 2025-06-02 19:42:43 +02:00
Florian Rival
90a34bd7d7 Merge branch 'master' into feat/commands 2025-05-30 17:20:03 +02:00
Florian Rival
31364bc487 Add support for automatically declaring object variables in AI generated events 2025-05-30 16:33:44 +02:00
Florian Rival
42b23f0d29 Add support for object group variables in simplified project + add support for result message in events generation 2025-05-30 12:07:16 +02:00
Florian Rival
b485bd0007 Ensure required behavior created when new behavior added from a editor function call, add property values and fix useless change in newly created object names 2025-05-29 21:49:13 +02:00
Florian Rival
c7a1883e1b Merge branch 'master' into feat/commands 2025-05-29 18:33:05 +02:00
Florian Rival
701b1d54e4 Add display of AI generated event details, render existing events always in English 2025-05-27 12:10:58 +02:00
Florian Rival
95864b41d8 Add support for sending object groups to the AI agent and add undeclared variables in generated events 2025-05-24 16:23:41 +02:00
Florian Rival
14a1a5d746 Add listing of properties when adding an object or behavior 2025-05-22 16:52:08 +02:00
Florian Rival
eb5acecb84 Add listing of behaviors when inspecting object in editor functions 2025-05-22 15:41:59 +02:00
Florian Rival
04b1309f50 Add support for background color in editor function calls 2025-05-22 00:32:45 +02:00
Florian Rival
babf0153a1 Add asset store support to AI agent 2025-05-21 23:15:43 +02:00
Florian Rival
a7b27b4d2d Fix past function calls wrongly displayed when a new result for same call_id arrives 2025-05-19 15:11:49 +02:00
Florian Rival
78c4f4b7a5 Add proper display of failed and ignored function calls 2025-05-18 18:17:09 +02:00
Florian Rival
96206aac29 Refactor events generation and AI chat messages display 2025-05-18 17:09:23 +02:00
Florian Rival
95a938d93b Merge branch 'master' into feat/commands 2025-05-16 10:24:28 +02:00
Florian Rival
0286ecd139 Add user friendly rendering of AI function calls 2025-05-15 18:32:28 +02:00
Florian Rival
297be166cd Install extension from generated events 2025-05-14 17:07:31 +02:00
Florian Rival
e626acdd21 Allow to choose between agent or chat for AI requests 2025-05-14 15:29:23 +02:00
Florian Rival
bf1f33b57a Adapt to updated endpoints 2025-05-13 13:42:42 +02:00
Florian Rival
890719b292 Add support for placement hint and new changes format for generated events 2025-05-09 16:47:56 +02:00
Florian Rival
e4815a1cb6 Merge branch 'master' into feat/commands 2025-05-08 12:28:04 +02:00
Florian Rival
d51cc42a64 Improve messaging of capabilities 2025-05-08 12:27:53 +02:00
Florian Rival
39a41e486d Implement insertion/replacement of events 2025-05-07 17:52:33 +02:00
Florian Rival
6e857629bf Implement existingEventsAsText for ai events generation 2025-05-06 19:17:51 +02:00
Florian Rival
67a81549d1 Add text renderer for events 2025-05-04 17:48:24 +02:00
Florian Rival
5f1c879b4c Refactor 2025-05-04 16:09:29 +02:00
Florian Rival
521cfe2f97 Allow to continue after pausing the AI agent 2025-05-03 18:20:09 +02:00
Florian Rival
e4cc5023f8 Improve display of banner for working AI 2025-05-03 17:30:26 +02:00
Florian Rival
5f983bef1c Refactor function calls and ensure extension is installed when adding a behavior 2025-05-03 14:40:22 +02:00
Florian Rival
3878e99d4f Improve styling 2025-05-03 12:40:20 +02:00
Florian Rival
c9086554e2 Add function to add/edit variable 2025-05-01 19:52:20 +02:00
Florian Rival
69e207fab4 WIP
- Auto process
- Improve behavior type not correct should send an error message
2025-04-30 16:26:06 +02:00
Florian Rival
bdc674eda3 Hide hidden properties from function calls 2025-04-29 18:01:31 +02:00
Florian Rival
d28b0c650d Merge branch 'master' into feat/commands 2025-04-29 10:48:16 +02:00
Florian Rival
78681eaba2 Add support for ignoring function calls 2025-04-29 09:11:39 +02:00
Florian Rival
48fa608c17 WIP: UI for function calls + events generation + rework AI requests UI to support multiple in parallels properly 2025-04-26 15:00:18 +02:00
Florian Rival
6acecae77d WIP: prototype function calls 2025-04-24 17:05:33 +02:00
Florian Rival
0a30e1e870 Extract SafeExtractor as a separate file 2025-04-13 14:40:56 +02:00
178 changed files with 703 additions and 2321 deletions

View File

@@ -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:

View File

@@ -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

View File

@@ -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_)"),
"",

View File

@@ -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"))

View File

@@ -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);
}
}
}
}

View File

@@ -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 {

View File

@@ -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:

View File

@@ -34,6 +34,7 @@ namespace gdjs {
objectData: gdjs.Object3DData & gdjs.CustomObjectConfiguration
) {
super(parent, objectData);
this._renderer.reinitialize(this, parent);
}
protected override _createRender() {

View File

@@ -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() {

View File

@@ -14,7 +14,6 @@ describe('gdjs.AnchorRuntimeBehavior', () => {
effects: [],
content: {},
childrenContent: {},
isInnerAreaFollowingParentSize: false,
});
runtimeScene.addObject(customObject);
customObject.setPosition(500, 250);

View File

@@ -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")

View File

@@ -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');

View File

@@ -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

View File

@@ -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)

View File

@@ -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();
}

View File

@@ -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;
}

View File

@@ -8,6 +8,7 @@ namespace gdjs {
objectData: ObjectData & CustomObjectConfiguration
) {
super(parent, objectData);
this.getRenderer().reinitialize(this, parent);
}
protected override _createRender(): gdjs.CustomRuntimeObject2DRenderer {

View File

@@ -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,

View File

@@ -811,8 +811,6 @@ namespace gdjs {
this._objectsCtor = new Hashtable();
this._allInstancesList = [];
this._instancesRemoved = [];
this._layersCameraCoordinates = {};
this._initialBehaviorSharedData = new Hashtable();
}
}
}

View File

@@ -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

View File

@@ -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() {

View File

@@ -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];

View File

@@ -16,7 +16,6 @@ describe('gdjs.CustomRuntimeObject', function () {
name: 'MyCustomObject',
type: 'MyExtension::MyEventsBasedObject',
variant: '',
isInnerAreaFollowingParentSize: false,
variables: [],
behaviors: [],
effects: [],

View File

@@ -100,7 +100,6 @@ describe('gdjs.HotReloader._hotReloadRuntimeGame', () => {
effects: [],
content: {},
childrenContent: {},
isInnerAreaFollowingParentSize: false,
};
/** @type {LayerData} */

View File

@@ -1405,7 +1405,6 @@ interface InitialInstance {
double GetCustomDepth();
[Ref] InitialInstance ResetPersistentUuid();
[Const, Ref] DOMString GetPersistentUuid();
void UpdateCustomProperty(
[Const] DOMString name,

View File

@@ -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;

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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}>

View File

@@ -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>
);
}
);

View File

@@ -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>

View File

@@ -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>
);

View File

@@ -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(

View File

@@ -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 {

View File

@@ -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,

View File

@@ -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();

View File

@@ -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:

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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.

View File

@@ -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}
/>
)}
</>

View File

@@ -656,7 +656,6 @@ const PrivateAssetPackInformationPage = ({
analyticsMetadata: {
reason: 'Claim asset pack',
recommendedPlanId: 'gdevelop_gold',
placementId: 'claim-asset-pack',
},
filter: 'individual',
})

View File

@@ -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 {

View File

@@ -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,
|};

View File

@@ -174,7 +174,6 @@ const LockedCourseChapterPreview = React.forwardRef<Props, HTMLDivElement>(
analyticsMetadata: {
reason: 'Unlock course chapter',
recommendedPlanId: 'gdevelop_silver',
placementId: 'unlock-course-chapter',
},
})
}

View File

@@ -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',

View File

@@ -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;
};

View File

@@ -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

View File

@@ -89,7 +89,7 @@ type Props = {|
eventsFunctionsExtension: gdEventsFunctionsExtension,
name: string
) => void,
onExtensionInstalled: (extensionNames: Array<string>) => void,
onExtensionInstalled: (extensionName: string) => void,
|};
type State = {|

View File

@@ -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 => {

View File

@@ -56,7 +56,7 @@ type Props = {|
i18n: I18nType,
canPasteInstructions: boolean, // Unused
onPasteInstructions: () => void, // Unused
onExtensionInstalled: (extensionNames: Array<string>) => void,
onExtensionInstalled: (extensionName: string) => void,
|};
/**

View File

@@ -154,7 +154,7 @@ type Props = {|
unsavedChanges?: ?UnsavedChanges,
isActive: boolean,
hotReloadPreviewButtonProps: HotReloadPreviewButtonProps,
onExtensionInstalled: (extensionNames: Array<string>) => void,
onExtensionInstalled: (extensionName: string) => void,
|};
type ComponentProps = {|

View File

@@ -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
/>

View File

@@ -326,7 +326,6 @@ const InviteHome = ({ cloudProjectId }: Props) => {
<GetSubscriptionCard
subscriptionDialogOpeningReason="Add collaborators on project"
recommendedPlanIdIfNoSubscription="gdevelop_startup"
placementId="invite-collaborators"
>
<Text>
<Trans>

View File

@@ -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>

View File

@@ -256,7 +256,6 @@ function LeaderboardOptionsDialog({
<GetSubscriptionCard
subscriptionDialogOpeningReason="Leaderboard customization"
recommendedPlanIdIfNoSubscription="gdevelop_startup"
placementId="leaderboards-customization"
>
<Line>
<Column noMargin>

View File

@@ -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>

View File

@@ -98,7 +98,6 @@ const ServicesWidget = ({
analyticsMetadata: {
reason: 'Leaderboard count per game limit reached',
recommendedPlanId: 'gdevelop_silver',
placementId: 'leaderboards',
},
})
}

View File

@@ -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,

View File

@@ -105,7 +105,6 @@ export class DebuggerEditorContainer extends React.Component<
}
id="Debugger"
title={<Trans>Debugger</Trans>}
placementId="debugger"
mode="try"
/>
</React.Fragment>

View File

@@ -55,7 +55,6 @@ export const MaxProjectCountAlertMessage = ({ margin }: Props) => {
}
hideButton={!canMaximumCountBeIncreased}
recommendedPlanIdIfNoSubscription="gdevelop_silver"
placementId="max-projects-reached"
>
<Line>
<Column noMargin expand>

View File

@@ -241,7 +241,6 @@ const EducationMarketingSection = ({
analyticsMetadata: {
reason: 'Callout in Classroom tab',
recommendedPlanId: 'gdevelop_education',
placementId: 'education',
},
});
},

View File

@@ -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>

View File

@@ -638,7 +638,6 @@ const ManageEducationAccountDialog = ({ onClose }: Props) => {
analyticsMetadata: {
reason: 'Manage subscription as teacher',
recommendedPlanId: 'gdevelop_education',
placementId: 'education',
},
filter: 'education',
})

View File

@@ -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}
/>
);

View File

@@ -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;
};

View File

@@ -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',
},
]),

View File

@@ -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 />

View File

@@ -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,
|};

View File

@@ -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

View File

@@ -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>

View File

@@ -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' ||

View File

@@ -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 ? (

View File

@@ -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,
});

View File

@@ -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>

View File

@@ -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>

View File

@@ -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',
|};

View File

@@ -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>
)}

View File

@@ -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',
}),
},
{

View File

@@ -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,

View File

@@ -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();

View File

@@ -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>

View File

@@ -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

View File

@@ -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,

View File

@@ -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:

View File

@@ -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>

View File

@@ -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>

View File

@@ -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,

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -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
),
})}
/>
);
}}

View File

@@ -526,7 +526,7 @@
},
"default": {
"background-color": {
"value": "#434343"
"value": "rgba(255, 255, 255, 0.09)"
},
"error": {
"value": "#FF8569",

View File

@@ -517,7 +517,7 @@
},
"default": {
"background-color": {
"value": "#4d5362"
"value": "rgba(255, 255, 255, 0.09)"
},
"error": {
"value": "#FF8569",

View File

@@ -522,7 +522,7 @@
},
"default": {
"background-color": {
"value": "#3b3f46"
"value": "rgba(255, 255, 255, 0.09)"
},
"error": {
"value": "#FF8569",

View File

@@ -518,7 +518,7 @@
},
"default": {
"background-color": {
"value": "#333141"
"value": "rgba(255, 255, 255, 0.09)"
},
"error": {
"value": "#FF8569",

View File

@@ -519,7 +519,7 @@
},
"default": {
"background-color": {
"value": "#1b3e49"
"value": "rgba(255, 255, 255, 0.09)"
},
"error": {
"value": "#FF8569",

View File

@@ -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) {

View File

@@ -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