From 443c35c1c37145692c11f229d46329e8c8da4b88 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Nov 2025 15:52:51 +0000
Subject: [PATCH 1/7] Initial plan
From 26cf3d559738fe2593bd1bdac31da384988f69b6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Nov 2025 15:57:02 +0000
Subject: [PATCH 2/7] Initial plan for scene-less app startup
Co-authored-by: EbiseLutica <7106976+EbiseLutica@users.noreply.github.com>
---
"Promete.Docs/obj\\Debug/\\package.g.props" | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 "Promete.Docs/obj\\Debug/\\package.g.props"
diff --git "a/Promete.Docs/obj\\Debug/\\package.g.props" "b/Promete.Docs/obj\\Debug/\\package.g.props"
new file mode 100644
index 0000000..45637c6
--- /dev/null
+++ "b/Promete.Docs/obj\\Debug/\\package.g.props"
@@ -0,0 +1,21 @@
+
+
+
+ promete-docs
+ 1.0.0
+
+ astro dev
+ astro dev
+ astro build
+ astro preview
+ []
+
+ MIT
+ pnpm@8.15.1
+ ^0.29.2
+ ^4.16.14
+ ^1.49.0
+ ^2.1.0
+ ^1.81.0
+
+
\ No newline at end of file
From e57d22e9fa2756c5d2009808fe77700031af84af Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Nov 2025 16:04:49 +0000
Subject: [PATCH 3/7] Implement PrometeApp.Run() without scene parameter
Co-authored-by: EbiseLutica <7106976+EbiseLutica@users.noreply.github.com>
---
Promete.Test/ScenelessExampleTests.cs | 53 ++++++++++++++++++++++++
Promete.Test/ScenelessRunTests.cs | 59 +++++++++++++++++++++++++++
Promete/PrometeApp.cs | 30 ++++++++++++++
3 files changed, 142 insertions(+)
create mode 100644 Promete.Test/ScenelessExampleTests.cs
create mode 100644 Promete.Test/ScenelessRunTests.cs
diff --git a/Promete.Test/ScenelessExampleTests.cs b/Promete.Test/ScenelessExampleTests.cs
new file mode 100644
index 0000000..c51ac96
--- /dev/null
+++ b/Promete.Test/ScenelessExampleTests.cs
@@ -0,0 +1,53 @@
+using Promete.Headless;
+using Promete.Input;
+using Promete.Windowing;
+using System.Reflection;
+
+namespace Promete.Test;
+
+///
+/// Issue で示されたコード例と同様のテスト。
+/// シーンを使わずに PrometeApp を実行できることを確認する。
+///
+public class ScenelessExampleTests
+{
+ [Fact]
+ public void Example_ScenelessHelloWorld_ShouldHaveRequiredMethods()
+ {
+ // Arrange - Issue で示されたコード例と同様のパターン
+ var app = PrometeApp.Create()
+ .Use()
+ .Use()
+ .BuildWithHeadless();
+
+ var keyboard = app.GetPlugin();
+ var console = app.GetPlugin();
+
+ // Window イベントにハンドラを登録できることを確認
+ app.Window.Start += () =>
+ {
+ console.Print("Hello, world!");
+ };
+
+ app.Window.Update += () =>
+ {
+ // Update logic would go here
+ };
+
+ // Run() メソッドが存在することを確認
+ var runMethods = typeof(PrometeApp).GetMethods(BindingFlags.Public | BindingFlags.Instance)
+ .Where(m => m.Name == "Run" && m.GetParameters().Length == 0 && !m.IsGenericMethod);
+ Assert.Single(runMethods);
+
+ var runMethodsWithOpts = typeof(PrometeApp).GetMethods(BindingFlags.Public | BindingFlags.Instance)
+ .Where(m => m.Name == "Run" &&
+ m.GetParameters().Length == 1 &&
+ m.GetParameters()[0].ParameterType == typeof(WindowOptions) &&
+ !m.IsGenericMethod);
+ Assert.Single(runMethodsWithOpts);
+
+ // プラグインが正しく取得できることを確認
+ Assert.NotNull(keyboard);
+ Assert.NotNull(console);
+ }
+}
diff --git a/Promete.Test/ScenelessRunTests.cs b/Promete.Test/ScenelessRunTests.cs
new file mode 100644
index 0000000..5b45591
--- /dev/null
+++ b/Promete.Test/ScenelessRunTests.cs
@@ -0,0 +1,59 @@
+using FluentAssertions;
+using Promete.Headless;
+
+namespace Promete.Test;
+
+public class ScenelessRunTests
+{
+ [Fact]
+ public void Run_WithoutScene_ShouldHaveValidRoot()
+ {
+ // Arrange
+ var app = PrometeApp.Create()
+ .BuildWithHeadless();
+
+ // DefaultSceneの型を取得する
+ var defaultSceneType = typeof(PrometeApp).GetNestedType("DefaultScene", System.Reflection.BindingFlags.NonPublic)!;
+
+ // OnStartを手動で呼び出してDefaultSceneをロード
+ var onStartMethod = typeof(PrometeApp).GetMethod("OnStart", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!;
+ var genericOnStartMethod = onStartMethod.MakeGenericMethod(defaultSceneType);
+ genericOnStartMethod.Invoke(app, null);
+
+ // Assert
+ app.Root.Should().NotBeNull("Root container should be initialized even without explicit scene");
+ }
+
+ [Fact]
+ public void GetPlugin_ShouldWork_WhenRunningWithoutScene()
+ {
+ // Arrange
+ var app = PrometeApp.Create()
+ .Use()
+ .BuildWithHeadless();
+
+ // Act
+ var console = app.GetPlugin();
+
+ // Assert
+ console.Should().NotBeNull("Plugins should be accessible even when running without explicit scene");
+ }
+
+ [Fact]
+ public void DefaultScene_ShouldBeRegistered()
+ {
+ // Arrange
+ var app = PrometeApp.Create()
+ .BuildWithHeadless();
+
+ // DefaultSceneの型を取得する
+ var defaultSceneType = typeof(PrometeApp).GetNestedType("DefaultScene", System.Reflection.BindingFlags.NonPublic)!;
+
+ // Act - DefaultSceneのロードを試みる
+ var loadSceneMethod = typeof(PrometeApp).GetMethod("LoadScene", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance, [typeof(Type)])!;
+
+ // Assert - 例外がスローされないことを確認
+ var act = () => loadSceneMethod.Invoke(app, [defaultSceneType]);
+ act.Should().NotThrow("DefaultScene should be registered and loadable");
+ }
+}
diff --git a/Promete/PrometeApp.cs b/Promete/PrometeApp.cs
index d6a45a9..8559a0b 100644
--- a/Promete/PrometeApp.cs
+++ b/Promete/PrometeApp.cs
@@ -136,6 +136,25 @@ public int Run(WindowOptions opts) where TScene : Scene
return _statusCode;
}
+ ///
+ /// Promete アプリケーションをシーンなしで実行します。
+ ///
+ /// 終了ステータスコード。
+ public int Run()
+ {
+ return Run(WindowOptions.Default);
+ }
+
+ ///
+ /// Promete アプリケーションをシーンなしで実行します。
+ ///
+ /// ウィンドウのオプション。
+ /// 終了ステータスコード。
+ public int Run(WindowOptions opts)
+ {
+ return Run(opts);
+ }
+
///
/// 指定したステータスコードで Promete アプリケーションを終了します。
///
@@ -379,6 +398,9 @@ private void ProcessNextFrameQueue()
private void RegisterAllScenes()
{
+ // DefaultScene を明示的に登録
+ _services.AddTransient();
+
var asm = Assembly.GetEntryAssembly() ?? throw new InvalidOperationException("There is no entry assembly.");
// Scene 派生クラスを全て取得する
var types = asm.GetTypes();
@@ -403,6 +425,14 @@ private Scene GetScene(Type scene)
///
public event Action? SceneWillChange;
+ ///
+ /// シーンを使用せずにアプリケーションを実行する際に使用されるデフォルトの空のシーン。
+ ///
+ [IgnoredScene]
+ private sealed class DefaultScene : Scene
+ {
+ }
+
///
/// Promete アプリケーションを構築するためのビルダークラスです。
///
From 083a23c0763a9964ab23bd44eb7cc07dc71d60a0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Nov 2025 16:06:10 +0000
Subject: [PATCH 4/7] Add demo example for sceneless app
Co-authored-by: EbiseLutica <7106976+EbiseLutica@users.noreply.github.com>
---
Promete.Example/examples/ScenelessDemo.cs | 53 +++++++++++++++++++++++
1 file changed, 53 insertions(+)
create mode 100644 Promete.Example/examples/ScenelessDemo.cs
diff --git a/Promete.Example/examples/ScenelessDemo.cs b/Promete.Example/examples/ScenelessDemo.cs
new file mode 100644
index 0000000..b62fabd
--- /dev/null
+++ b/Promete.Example/examples/ScenelessDemo.cs
@@ -0,0 +1,53 @@
+using Promete.Example.Kernel;
+using Promete.Input;
+
+namespace Promete.Example.examples;
+
+///
+/// シーンを使わずに PrometeApp を実行するデモ
+///
+[Demo("/sceneless", "シーンを使わずに起動")]
+public class ScenelessDemo(Keyboard keyboard, ConsoleLayer console) : Scene
+{
+ private int frameCount;
+
+ public override void OnStart()
+ {
+ console.Print("Sceneless Demo");
+ console.Print("このデモは、将来的にはシーンなしで実行できます。");
+ console.Print("");
+ console.Print("例:");
+ console.Print("var app = PrometeApp.Create()");
+ console.Print(" .Use()");
+ console.Print(" .Use()");
+ console.Print(" .BuildWithOpenGLDesktop();");
+ console.Print("");
+ console.Print("app.Window.Start += () => {");
+ console.Print(" console.Print(\"Hello, world!\");");
+ console.Print("};");
+ console.Print("");
+ console.Print("app.Window.Update += () => {");
+ console.Print(" if (keyboard.Escape.IsKeyDown)");
+ console.Print(" app.Exit(0);");
+ console.Print("};");
+ console.Print("");
+ console.Print("return app.Run();");
+ console.Print("");
+ console.Print("[ESC] 戻る");
+ }
+
+ public override void OnUpdate()
+ {
+ frameCount++;
+
+ if (frameCount % 60 == 0)
+ {
+ console.Print($"Frame: {frameCount}");
+ }
+
+ if (keyboard.Escape.IsKeyUp)
+ {
+ App.LoadScene();
+ }
+ }
+}
From 3a65e3a9d5e3130377d607931d32dc223dc2958b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Wed, 5 Nov 2025 16:10:32 +0000
Subject: [PATCH 5/7] Refactor tests to reduce code duplication
Co-authored-by: EbiseLutica <7106976+EbiseLutica@users.noreply.github.com>
---
Promete.Test/ScenelessRunTests.cs | 23 +++++++++++++----------
1 file changed, 13 insertions(+), 10 deletions(-)
diff --git a/Promete.Test/ScenelessRunTests.cs b/Promete.Test/ScenelessRunTests.cs
index 5b45591..1e0adab 100644
--- a/Promete.Test/ScenelessRunTests.cs
+++ b/Promete.Test/ScenelessRunTests.cs
@@ -1,10 +1,19 @@
using FluentAssertions;
using Promete.Headless;
+using System.Reflection;
namespace Promete.Test;
public class ScenelessRunTests
{
+ ///
+ /// DefaultScene の型を取得するヘルパーメソッド
+ ///
+ private static Type GetDefaultSceneType()
+ {
+ return typeof(PrometeApp).GetNestedType("DefaultScene", BindingFlags.NonPublic)!;
+ }
+
[Fact]
public void Run_WithoutScene_ShouldHaveValidRoot()
{
@@ -12,12 +21,9 @@ public void Run_WithoutScene_ShouldHaveValidRoot()
var app = PrometeApp.Create()
.BuildWithHeadless();
- // DefaultSceneの型を取得する
- var defaultSceneType = typeof(PrometeApp).GetNestedType("DefaultScene", System.Reflection.BindingFlags.NonPublic)!;
-
// OnStartを手動で呼び出してDefaultSceneをロード
- var onStartMethod = typeof(PrometeApp).GetMethod("OnStart", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)!;
- var genericOnStartMethod = onStartMethod.MakeGenericMethod(defaultSceneType);
+ var onStartMethod = typeof(PrometeApp).GetMethod("OnStart", BindingFlags.NonPublic | BindingFlags.Instance)!;
+ var genericOnStartMethod = onStartMethod.MakeGenericMethod(GetDefaultSceneType());
genericOnStartMethod.Invoke(app, null);
// Assert
@@ -46,14 +52,11 @@ public void DefaultScene_ShouldBeRegistered()
var app = PrometeApp.Create()
.BuildWithHeadless();
- // DefaultSceneの型を取得する
- var defaultSceneType = typeof(PrometeApp).GetNestedType("DefaultScene", System.Reflection.BindingFlags.NonPublic)!;
-
// Act - DefaultSceneのロードを試みる
- var loadSceneMethod = typeof(PrometeApp).GetMethod("LoadScene", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Instance, [typeof(Type)])!;
+ var loadSceneMethod = typeof(PrometeApp).GetMethod("LoadScene", BindingFlags.Public | BindingFlags.Instance, [typeof(Type)])!;
// Assert - 例外がスローされないことを確認
- var act = () => loadSceneMethod.Invoke(app, [defaultSceneType]);
+ var act = () => loadSceneMethod.Invoke(app, [GetDefaultSceneType()]);
act.Should().NotThrow("DefaultScene should be registered and loadable");
}
}
From a185a917cb084a70d290e6ec27aea3978723138a Mon Sep 17 00:00:00 2001
From: Ebise Lutica <7106976+EbiseLutica@users.noreply.github.com>
Date: Thu, 6 Nov 2025 01:43:20 +0900
Subject: [PATCH 6/7] Delete Promete.Docs/obj\Debug/\package.g.props
---
"Promete.Docs/obj\\Debug/\\package.g.props" | 21 ---------------------
1 file changed, 21 deletions(-)
delete mode 100644 "Promete.Docs/obj\\Debug/\\package.g.props"
diff --git "a/Promete.Docs/obj\\Debug/\\package.g.props" "b/Promete.Docs/obj\\Debug/\\package.g.props"
deleted file mode 100644
index 45637c6..0000000
--- "a/Promete.Docs/obj\\Debug/\\package.g.props"
+++ /dev/null
@@ -1,21 +0,0 @@
-
-
-
- promete-docs
- 1.0.0
-
- astro dev
- astro dev
- astro build
- astro preview
- []
-
- MIT
- pnpm@8.15.1
- ^0.29.2
- ^4.16.14
- ^1.49.0
- ^2.1.0
- ^1.81.0
-
-
\ No newline at end of file
From 15f09960e20226b118347a064ce65e2426fe47a5 Mon Sep 17 00:00:00 2001
From: Ebise Lutica <7106976+EbiseLutica@users.noreply.github.com>
Date: Thu, 6 Nov 2025 01:43:34 +0900
Subject: [PATCH 7/7] Delete Promete.Example/examples/ScenelessDemo.cs
---
Promete.Example/examples/ScenelessDemo.cs | 53 -----------------------
1 file changed, 53 deletions(-)
delete mode 100644 Promete.Example/examples/ScenelessDemo.cs
diff --git a/Promete.Example/examples/ScenelessDemo.cs b/Promete.Example/examples/ScenelessDemo.cs
deleted file mode 100644
index b62fabd..0000000
--- a/Promete.Example/examples/ScenelessDemo.cs
+++ /dev/null
@@ -1,53 +0,0 @@
-using Promete.Example.Kernel;
-using Promete.Input;
-
-namespace Promete.Example.examples;
-
-///
-/// シーンを使わずに PrometeApp を実行するデモ
-///
-[Demo("/sceneless", "シーンを使わずに起動")]
-public class ScenelessDemo(Keyboard keyboard, ConsoleLayer console) : Scene
-{
- private int frameCount;
-
- public override void OnStart()
- {
- console.Print("Sceneless Demo");
- console.Print("このデモは、将来的にはシーンなしで実行できます。");
- console.Print("");
- console.Print("例:");
- console.Print("var app = PrometeApp.Create()");
- console.Print(" .Use()");
- console.Print(" .Use()");
- console.Print(" .BuildWithOpenGLDesktop();");
- console.Print("");
- console.Print("app.Window.Start += () => {");
- console.Print(" console.Print(\"Hello, world!\");");
- console.Print("};");
- console.Print("");
- console.Print("app.Window.Update += () => {");
- console.Print(" if (keyboard.Escape.IsKeyDown)");
- console.Print(" app.Exit(0);");
- console.Print("};");
- console.Print("");
- console.Print("return app.Run();");
- console.Print("");
- console.Print("[ESC] 戻る");
- }
-
- public override void OnUpdate()
- {
- frameCount++;
-
- if (frameCount % 60 == 0)
- {
- console.Print($"Frame: {frameCount}");
- }
-
- if (keyboard.Escape.IsKeyUp)
- {
- App.LoadScene();
- }
- }
-}