Skip to content
Closed
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions examples/simple_chat/macos/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
E9FEBF992037BBC39FEBE174 /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = FA2662D235B4AB1963BAB5F4 /* GoogleService-Info.plist */; };
F11A3C6CCBEE37426C146C43 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BE242B4E6EAF01C24056F2F7 /* Pods_RunnerTests.framework */; };
/* End PBXBuildFile section */

Expand Down Expand Up @@ -88,6 +89,7 @@
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
BE242B4E6EAF01C24056F2F7 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C6F17EC3323727C4F655EBBA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
FA2662D235B4AB1963BAB5F4 /* GoogleService-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; name = "GoogleService-Info.plist"; path = "Runner/GoogleService-Info.plist"; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -138,6 +140,7 @@
33CC10EE2044A3C60003C045 /* Products */,
D73912EC22F37F3D000D13A0 /* Frameworks */,
CB35E92F67C0BC65B6874AAC /* Pods */,
FA2662D235B4AB1963BAB5F4 /* GoogleService-Info.plist */,
);
sourceTree = "<group>";
};
Expand Down Expand Up @@ -317,6 +320,7 @@
files = (
33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
E9FEBF992037BBC39FEBE174 /* GoogleService-Info.plist in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down
11 changes: 11 additions & 0 deletions examples/travel_app/linux/flutter/generated_plugin_registrant.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// Generated file. Do not edit.
//

// clang-format off

#include "generated_plugin_registrant.h"


void fl_register_plugins(FlPluginRegistry* registry) {
}
15 changes: 15 additions & 0 deletions examples/travel_app/linux/flutter/generated_plugin_registrant.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// Generated file. Do not edit.
//

// clang-format off

#ifndef GENERATED_PLUGIN_REGISTRANT_
#define GENERATED_PLUGIN_REGISTRANT_

#include <flutter_linux/flutter_linux.h>

// Registers Flutter plugins.
void fl_register_plugins(FlPluginRegistry* registry);

#endif // GENERATED_PLUGIN_REGISTRANT_
23 changes: 23 additions & 0 deletions examples/travel_app/linux/flutter/generated_plugins.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#
# Generated file, do not edit.
#

list(APPEND FLUTTER_PLUGIN_LIST
)

list(APPEND FLUTTER_FFI_PLUGIN_LIST
)

set(PLUGIN_BUNDLED_LIBRARIES)

foreach(plugin ${FLUTTER_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
endforeach(plugin)

foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})
add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})
list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})
endforeach(ffi_plugin)
10 changes: 7 additions & 3 deletions packages/genui/lib/src/core/ui_tools.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ class SurfaceUpdateTool extends AiTool<JsonMap> {
required this.handleMessage,
required Catalog catalog,
required this.configuration,
required SurfaceUpdateMode updateMode,
super.name = 'surfaceUpdate',
super.description = 'Updates a surface with a new set of components.',
}) : super(
name: 'surfaceUpdate',
description: 'Updates a surface with a new set of components.',
parameters: A2uiSchemas.surfaceUpdateSchema(catalog),
parameters: A2uiSchemas.surfaceUpdateSchema(
catalog,
updateMode: updateMode,
),
);

/// The callback to invoke when adding or updating a surface.
Expand Down
106 changes: 68 additions & 38 deletions packages/genui/lib/src/model/a2ui_schemas.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ import 'package:json_schema_builder/json_schema_builder.dart';
import 'catalog.dart';
import 'tools.dart';

/// Defines the allowed operations for a surface update tool.
enum SurfaceUpdateMode {
/// The tool can only create new surfaces.
create,

/// The tool can only update existing surfaces.
update,

/// The tool can both create and update surfaces.
both,
}

/// Provides a set of pre-defined, reusable schema objects for common
/// A2UI patterns, simplifying the creation of CatalogItem definitions.
class A2uiSchemas {
Expand Down Expand Up @@ -177,46 +189,64 @@ class A2uiSchemas {

/// Schema for a `surfaceUpdate` message which defines the components to be
/// rendered on a surface.
static Schema surfaceUpdateSchema(Catalog catalog) => S.object(
properties: {
surfaceIdKey: S.string(
description:
static Schema surfaceUpdateSchema(
Catalog catalog, {
SurfaceUpdateMode updateMode = SurfaceUpdateMode.both,
}) {
final String surfaceIdDescription;
switch (updateMode) {
case SurfaceUpdateMode.create:
surfaceIdDescription =
'The unique identifier for the new UI surface to create. This '
'*must* be a new, unique identifier.';
break;
case SurfaceUpdateMode.update:
surfaceIdDescription =
'The unique identifier for the existing UI surface to update.';
break;
case SurfaceUpdateMode.both:
surfaceIdDescription =
'The unique identifier for the UI surface to create or '
'update. If you are adding a new surface this *must* be a '
'new, unique identified that has never been used for any '
'existing surfaces shown.',
),
'components': S.list(
description: 'A list of component definitions.',
minItems: 1,
items: S.object(
description:
'Represents a *single* component in a UI widget tree. '
'This component could be one of many supported types.',
properties: {
'id': S.string(),
'weight': S.integer(
description:
'Optional layout weight for use in Row/Column children.',
),
'component': S.object(
description:
'''A wrapper object that MUST contain exactly one key, which is the name of the component type (e.g., 'Text'). The value is an object containing the properties for that specific component.''',
properties: {
for (var entry
in ((catalog.definition as ObjectSchema)
.properties!['components']!
as ObjectSchema)
.properties!
.entries)
entry.key: entry.value,
},
),
},
required: ['id', 'component'],
'existing surfaces shown.';
break;
}
Comment on lines 222 to 241
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

For improved conciseness and readability, you could refactor this switch statement into a switch expression, which is a feature available in modern Dart. This would make the assignment of surfaceIdDescription more direct and the code more idiomatic.

    final String surfaceIdDescription = switch (updateMode) {
      SurfaceUpdateMode.create =>
        'The unique identifier for the new UI surface to create. This '
        '*must* be a new, unique identifier.',
      SurfaceUpdateMode.update =>
        'The unique identifier for the existing UI surface to update.',
      SurfaceUpdateMode.both =>
        'The unique identifier for the UI surface to create or '
        'update. If you are adding a new surface this *must* be a '
        'new, unique identified that has never been used for any '
        'existing surfaces shown.',
    };

return S.object(
properties: {
surfaceIdKey: S.string(description: surfaceIdDescription),
'components': S.list(
description: 'A list of component definitions.',
minItems: 1,
items: S.object(
description:
'Represents a *single* component in a UI widget tree. '
'This component could be one of many supported types.',
properties: {
'id': S.string(),
'weight': S.integer(
description:
'Optional layout weight for use in Row/Column children.',
),
'component': S.object(
description:
'''A wrapper object that MUST contain exactly one key, which is the name of the component type (e.g., 'Text'). The value is an object containing the properties for that specific component.''',
properties: {
for (var entry
in ((catalog.definition as ObjectSchema)
.properties!['components']!
as ObjectSchema)
.properties!
.entries)
entry.key: entry.value,
},
),
},
required: ['id', 'component'],
),
),
),
},
required: [surfaceIdKey, 'components'],
);
},
required: [surfaceIdKey, 'components'],
);
}
}
2 changes: 2 additions & 0 deletions packages/genui/test/core/ui_tools_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:genui/src/core/genui_configuration.dart';
import 'package:genui/src/core/ui_tools.dart';
import 'package:genui/src/model/a2ui_message.dart';
import 'package:genui/src/model/a2ui_schemas.dart';
import 'package:genui/src/model/catalog.dart';
import 'package:genui/src/model/catalog_item.dart';
import 'package:genui/src/model/tools.dart';
Expand All @@ -33,6 +34,7 @@ void main() {
),
]),
configuration: const GenUiConfiguration(),
updateMode: SurfaceUpdateMode.both,
);

final Map<String, Object> args = {
Expand Down
1 change: 1 addition & 0 deletions packages/genui/test/ui_tools_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ void main() {
handleMessage: genUiManager.handleMessage,
catalog: catalog,
configuration: const GenUiConfiguration(),
updateMode: SurfaceUpdateMode.both,
);

final Map<String, Object> args = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -343,15 +343,27 @@ class FirebaseAiContentGenerator implements ContentGenerator {
final adapter = GeminiSchemaAdapter();

final List<AiTool<JsonMap>> availableTools = [
if (configuration.actions.allowCreate ||
configuration.actions.allowUpdate) ...[
if (configuration.actions.allowCreate)
SurfaceUpdateTool(
handleMessage: _a2uiMessageController.add,
catalog: catalog,
configuration: configuration,
updateMode: SurfaceUpdateMode.create,
name: 'surfaceCreate',
description: 'Creates a new UI surface.',
),
if (configuration.actions.allowUpdate)
SurfaceUpdateTool(
handleMessage: _a2uiMessageController.add,
catalog: catalog,
configuration: configuration,
updateMode: SurfaceUpdateMode.update,
name: 'surfaceUpdate',
description: 'Updates an existing UI surface.',
),
if (configuration.actions.allowCreate ||
configuration.actions.allowUpdate)
BeginRenderingTool(handleMessage: _a2uiMessageController.add),
],
if (configuration.actions.allowDelete)
DeleteSurfaceTool(handleMessage: _a2uiMessageController.add),
...additionalTools,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -344,15 +344,27 @@ class GoogleGenerativeAiContentGenerator implements ContentGenerator {

try {
final availableTools = [
if (configuration.actions.allowCreate ||
configuration.actions.allowUpdate) ...[
if (configuration.actions.allowCreate)
SurfaceUpdateTool(
handleMessage: _a2uiMessageController.add,
catalog: catalog,
configuration: configuration,
updateMode: SurfaceUpdateMode.create,
name: 'surfaceCreate',
description: 'Creates a new UI surface.',
),
if (configuration.actions.allowUpdate)
SurfaceUpdateTool(
handleMessage: _a2uiMessageController.add,
catalog: catalog,
configuration: configuration,
updateMode: SurfaceUpdateMode.update,
name: 'surfaceUpdate',
description: 'Updates an existing UI surface.',
),
if (configuration.actions.allowCreate ||
configuration.actions.allowUpdate)
BeginRenderingTool(handleMessage: _a2uiMessageController.add),
],
if (configuration.actions.allowDelete)
DeleteSurfaceTool(handleMessage: _a2uiMessageController.add),
...additionalTools,
Expand Down
Loading