Skip to content

Commit

Permalink
#29 XMLを表示
Browse files Browse the repository at this point in the history
  • Loading branch information
miyaji255 committed Apr 29, 2024
1 parent 56c0ab6 commit cb6de09
Show file tree
Hide file tree
Showing 11 changed files with 161 additions and 63 deletions.
32 changes: 19 additions & 13 deletions Epub/KoeBook.Epub/Services/AiStoryAnalyzerService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,29 @@ public partial class AiStoryAnalyzerService(ISplitBraceService splitBraceService

public EpubDocument CreateEpubDocument(AiStory aiStory, Guid id)
{
int sectionNumber = 1;
return new EpubDocument(aiStory.Title, "AI", "", id)
{
Chapters = [new Chapter()
{
Sections = aiStory.Sections.Select(s => new Section($"第{sectionNumber++}章")
{
Elements = s.Paragraphs.SelectMany(p =>
_splitBraceService.SplitBrace(p.GetText())
.Zip(_splitBraceService.SplitBrace(p.GetScript()))
.Select(Element (p) => new Paragraph
{
Text = p.First,
ScriptLine = new(p.Second, "", "")
})
).ToList(),
}).ToList(),
Sections = [
new Section("本編")
{
Elements = aiStory.Lines.SelectMany(s =>
s.SelectMany(p => _splitBraceService.SplitBrace(p.GetText())
.Zip(_splitBraceService.SplitBrace(p.GetScript()))
.Select(Element (p) => new Paragraph
{
Text = p.First,
ScriptLine = new(p.Second, "", "")
}))
.Append(new Paragraph()
{
Text = "",
ScriptLine = new("", "", "")
})
).ToList(),
}
]
}]
};
}
Expand Down
1 change: 1 addition & 0 deletions KoeBook.Core/Contracts/Services/IStoryCreatorService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ namespace KoeBook.Core.Contracts.Services;

public interface IStoryCreaterService
{
/// <returns>XML</returns>
public ValueTask<string> CreateStoryAsync(StoryGenre genre, string intruction, CancellationToken cancellationToken);
}
20 changes: 9 additions & 11 deletions KoeBook.Core/Models/AiStory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,21 @@
namespace KoeBook.Models;

[XmlRoot("Book")]
public record AiStory(
[property: XmlElement("Title", typeof(string), IsNullable = false)] string Title,
[property: XmlArray("Content", IsNullable = false), XmlArrayItem("Section", IsNullable = false)] AiStory.Section[] Sections)
public class AiStory
{
private AiStory() : this("", []) { }

public record Section(
[property: XmlArrayItem("Paragraph", IsNullable = false)] Paragraph[] Paragraphs)
{
private Section() : this([]) { }
}
[XmlElement("Title", typeof(string), IsNullable = false)]
public string Title { get; init; } = "";

[XmlArray("Content", IsNullable = false)]
[XmlArrayItem("Section", IsNullable = false)]
[XmlArrayItem("Paragraph", IsNullable = false, NestingLevel = 1)]
public Line[][] Lines { get; init; } = [];

public record Paragraph(
public record Line(
[property: XmlElement("Text", typeof(TextElement), IsNullable = false), XmlElement("Ruby", typeof(Ruby), IsNullable = false)] InlineElement[] Inlines)
{
private Paragraph() : this([]) { }
private Line() : this([]) { }

public string GetText() => string.Concat(Inlines.Select(e => e.Text));

Expand Down
2 changes: 2 additions & 0 deletions KoeBook/App.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ public App()
services.AddSingleton<ISoundGenerationSelectorService, SoundGenerationSelectorServiceMock>();
if (mockOptions.ISoundGenerationService.HasValue && mockOptions.ISoundGenerationService.Value)
services.AddSingleton<ISoundGenerationService, SoundGenerationServiceMock>();
if(mockOptions.IStoryCreaterService.HasValue && mockOptions.IStoryCreaterService.Value)
services.AddSingleton<IStoryCreaterService, StoryCreaterServiceMock>();
})
.Build();

Expand Down
2 changes: 2 additions & 0 deletions KoeBook/Models/MockOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ internal class MockOptions
public bool? IAnalyzerService { get; set; }

public bool? IEpubGenerateService { get; set; }

public bool? IStoryCreaterService { get; set; }
}
34 changes: 34 additions & 0 deletions KoeBook/Services/CoreMocks/StoryCreaterServiceMock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
using KoeBook.Core.Contracts.Services;
using KoeBook.Core.Models;

namespace KoeBook.Services.CoreMocks
{
public class StoryCreaterServiceMock : IStoryCreaterService
{
public ValueTask<string> CreateStoryAsync(StoryGenre genre, string intruction, CancellationToken cancellationToken)
{
return ValueTask.FromResult("""
<?xml version="1.0" encoding="UTF-8"?>
<Book>
<Title>境界線の向こう側</Title>
<Content>
<Section>
<Paragraph><Text>高校2年の夏、バレー部のエースで</Text><Ruby><Rb>端正</Rb><Rt>はんせい</Rt></Ruby><Text>な顔立ちの山田祐樹は、バスケ部のキャプテンで</Text><Ruby><Rb>凛</Rb><Rt>りん</Rt></Ruby><Text>とした佇まいの田中麻衣に密かに想いを寄せていた。しかし、両者の部活</Text><Ruby><Rb>仲間</Rb><Rt>なかま</Rt></Ruby><Text>たちの目を</Text><Ruby><Rb>憚</Rb><Rt>はばか</Rt></Ruby><Text>り、互いに素振りも見せずにいた。</Text></Paragraph>
<Paragraph><Text>高校2年の夏、バレー部のエースで</Text><Ruby><Rb>端正</Rb><Rt>はんせい</Rt></Ruby><Text>な顔立ちの山田祐樹は、バスケ部のキャプテンで</Text><Ruby><Rb>凛</Rb><Rt>りん</Rt></Ruby><Text>とした佇まいの田中麻衣に密かに想いを寄せていた。しかし、両者の部活</Text><Ruby><Rb>仲間</Rb><Rt>なかま</Rt></Ruby><Text>たちの目を</Text><Ruby><Rb>憚</Rb><Rt>はばか</Rt></Ruby><Text>り、互いに素振りも見せずにいた。</Text></Paragraph>
<Paragraph><Text>高校2年の夏、バレー部のエースで</Text><Ruby><Rb>端正</Rb><Rt>はんせい</Rt></Ruby><Text>な顔立ちの山田祐樹は、バスケ部のキャプテンで</Text><Ruby><Rb>凛</Rb><Rt>りん</Rt></Ruby><Text>とした佇まいの田中麻衣に密かに想いを寄せていた。しかし、両者の部活</Text><Ruby><Rb>仲間</Rb><Rt>なかま</Rt></Ruby><Text>たちの目を</Text><Ruby><Rb>憚</Rb><Rt>はばか</Rt></Ruby><Text>り、互いに素振りも見せずにいた。</Text></Paragraph>
<Paragraph><Text>高校2年の夏、バレー部のエースで</Text><Ruby><Rb>端正</Rb><Rt>はんせい</Rt></Ruby><Text>な顔立ちの山田祐樹は、バスケ部のキャプテンで</Text><Ruby><Rb>凛</Rb><Rt>りん</Rt></Ruby><Text>とした佇まいの田中麻衣に密かに想いを寄せていた。しかし、両者の部活</Text><Ruby><Rb>仲間</Rb><Rt>なかま</Rt></Ruby><Text>たちの目を</Text><Ruby><Rb>憚</Rb><Rt>はばか</Rt></Ruby><Text>り、互いに素振りも見せずにいた。</Text></Paragraph>
</Section>
<Section>
<Paragraph><Text>高校2年の夏、バレー部のエースで</Text><Ruby><Rb>端正</Rb><Rt>はんせい</Rt></Ruby><Text>な顔立ちの山田祐樹は、バスケ部のキャプテンで</Text><Ruby><Rb>凛</Rb><Rt>りん</Rt></Ruby><Text>とした佇まいの田中麻衣に密かに想いを寄せていた。しかし、両者の部活</Text><Ruby><Rb>仲間</Rb><Rt>なかま</Rt></Ruby><Text>たちの目を</Text><Ruby><Rb>憚</Rb><Rt>はばか</Rt></Ruby><Text>り、互いに素振りも見せずにいた。</Text></Paragraph>
<Paragraph><Text>高校2年の夏、バレー部のエースで</Text><Ruby><Rb>端正</Rb><Rt>はんせい</Rt></Ruby><Text>な顔立ちの山田祐樹は、バスケ部のキャプテンで</Text><Ruby><Rb>凛</Rb><Rt>りん</Rt></Ruby><Text>とした佇まいの田中麻衣に密かに想いを寄せていた。しかし、両者の部活</Text><Ruby><Rb>仲間</Rb><Rt>なかま</Rt></Ruby><Text>たちの目を</Text><Ruby><Rb>憚</Rb><Rt>はばか</Rt></Ruby><Text>り、互いに素振りも見せずにいた。</Text></Paragraph>
<Paragraph><Text>高校2年の夏、バレー部のエースで</Text><Ruby><Rb>端正</Rb><Rt>はんせい</Rt></Ruby><Text>な顔立ちの山田祐樹は、バスケ部のキャプテンで</Text><Ruby><Rb>凛</Rb><Rt>りん</Rt></Ruby><Text>とした佇まいの田中麻衣に密かに想いを寄せていた。しかし、両者の部活</Text><Ruby><Rb>仲間</Rb><Rt>なかま</Rt></Ruby><Text>たちの目を</Text><Ruby><Rb>憚</Rb><Rt>はばか</Rt></Ruby><Text>り、互いに素振りも見せずにいた。</Text></Paragraph>
<Paragraph><Text>高校2年の夏、バレー部のエースで</Text><Ruby><Rb>端正</Rb><Rt>はんせい</Rt></Ruby><Text>な顔立ちの山田祐樹は、バスケ部のキャプテンで</Text><Ruby><Rb>凛</Rb><Rt>りん</Rt></Ruby><Text>とした佇まいの田中麻衣に密かに想いを寄せていた。しかし、両者の部活</Text><Ruby><Rb>仲間</Rb><Rt>なかま</Rt></Ruby><Text>たちの目を</Text><Ruby><Rb>憚</Rb><Rt>はばか</Rt></Ruby><Text>り、互いに素振りも見せずにいた。</Text></Paragraph>
<Paragraph><Text>高校2年の夏、バレー部のエースで</Text><Ruby><Rb>端正</Rb><Rt>はんせい</Rt></Ruby><Text>な顔立ちの山田祐樹は、バスケ部のキャプテンで</Text><Ruby><Rb>凛</Rb><Rt>りん</Rt></Ruby><Text>とした佇まいの田中麻衣に密かに想いを寄せていた。しかし、両者の部活</Text><Ruby><Rb>仲間</Rb><Rt>なかま</Rt></Ruby><Text>たちの目を</Text><Ruby><Rb>憚</Rb><Rt>はばか</Rt></Ruby><Text>り、互いに素振りも見せずにいた。</Text></Paragraph>
<Paragraph><Text>高校2年の夏、バレー部のエースで</Text><Ruby><Rb>端正</Rb><Rt>はんせい</Rt></Ruby><Text>な顔立ちの山田祐樹は、バスケ部のキャプテンで</Text><Ruby><Rb>凛</Rb><Rt>りん</Rt></Ruby><Text>とした佇まいの田中麻衣に密かに想いを寄せていた。しかし、両者の部活</Text><Ruby><Rb>仲間</Rb><Rt>なかま</Rt></Ruby><Text>たちの目を</Text><Ruby><Rb>憚</Rb><Rt>はばか</Rt></Ruby><Text>り、互いに素振りも見せずにいた。</Text></Paragraph>
</Section>
</Content>
</Book>
""");
}
}
}
52 changes: 32 additions & 20 deletions KoeBook/ViewModels/CreateStoryViewModel.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
using System.Collections.Immutable;
using System.Xml.Serialization;
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using KoeBook.Contracts.Services;
using KoeBook.Core.Contracts.Services;
using KoeBook.Core.Models;
using KoeBook.Services;
using KoeBook.Models;

namespace KoeBook.ViewModels;

public sealed partial class CreateStoryViewModel : ObservableObject
{
private readonly IGenerationTaskService _generationTaskService;
private readonly IDialogService _dialogService;
private readonly IStoryCreaterService _storyCreaterService;
private readonly GenerationTaskRunnerService _generationTaskRunnerService;

public ImmutableArray<StoryGenre> Genres { get; } = [
new("青春小説", "学校生活、友情、恋愛など、若者の成長物語"),
new("青春小説", "学校生活、友情、恋愛など、若者の成長物語"),
new("ミステリー・サスペンス", "謎解きや犯罪、真相究明などのスリリングな物語"),
new("SF", "未来、科学技術、宇宙などを題材にした物語"),
new("ホラー", "恐怖や怪奇現象を扱った、読者の恐怖心をくすぐる物語"),
Expand All @@ -28,40 +31,49 @@ public sealed partial class CreateStoryViewModel : ObservableObject
private StoryGenre _selectedGenre;

[ObservableProperty]
[NotifyCanExecuteChangedFor(nameof(CreateStoryCommand))]
private string _instruction = "";

[ObservableProperty]
private string _storyText = """
# h1
## h2
### h3
[NotifyCanExecuteChangedFor(nameof(StartGenerateTaskCommand))]
[NotifyPropertyChangedFor(nameof(AiStoryTitle))]
private AiStory? _aiStory;

1. aaa
2. bbb
3. ccc
public string AiStoryTitle => AiStory?.Title ?? "";

---
""";

public CreateStoryViewModel(GenerationTaskRunnerService generationTaskRunnerService)
public CreateStoryViewModel(IGenerationTaskService generationTaskService, IDialogService dialogService, IStoryCreaterService storyCreaterService)
{
_selectedGenre = Genres[0];
_generationTaskRunnerService = generationTaskRunnerService;
//_storyCreaterService = storyCreaterService;
_storyCreaterService = null!;
_generationTaskService = generationTaskService;
_dialogService = dialogService;
_storyCreaterService = storyCreaterService;
}

public bool CanCreateStory => !string.IsNullOrWhiteSpace(Instruction);

[RelayCommand(CanExecute = nameof(CanCreateStory))]
private async Task OnCreateStoryAsync(CancellationToken cancellationToken)
{
StoryText = await _storyCreaterService.CreateStoryAsync(SelectedGenre, Instruction, cancellationToken);
using var sr = new StringReader(await _storyCreaterService.CreateStoryAsync(SelectedGenre, Instruction, cancellationToken));
var serializer = new XmlSerializer(typeof(AiStory));
try
{
AiStory = (AiStory?)serializer.Deserialize(sr);
}
catch (InvalidOperationException)
{
await _dialogService.ShowAsync("生成失敗", "AIによるコードの生成に失敗しました", "OK", cancellationToken);
}
}

[RelayCommand]
private async void OnStartGenerateTask(CancellationToken cancellationToken)
public bool CanStartGenerate => AiStory is not null;

[RelayCommand(CanExecute = nameof(CanStartGenerate))]
private void OnStartGenerateTask()
{
var aiStory = AiStory!;
AiStory = null;
_generationTaskService.Register(new GenerationTask(Guid.NewGuid(), aiStory, true));
}
}

35 changes: 25 additions & 10 deletions KoeBook/Views/CreateStoryPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,10 @@
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition MinHeight="220" />
<RowDefinition />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>

<TextBlock
Expand Down Expand Up @@ -51,10 +52,12 @@
<Button
Margin="{StaticResource SmallTopMargin}"
Content="物語を生成する"
Command="{x:Bind ViewModel.CreateStoryCommand}"
Width="160"/>
<Button
Margin="{StaticResource SmallTopMargin}"
Content="EPUBを生成する"
Command="{x:Bind ViewModel.StartGenerateTaskCommand}"
Width="160" />
</StackPanel>

Expand All @@ -70,20 +73,32 @@
TextWrapping="Wrap"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Margin="{StaticResource SmallTopMargin}"
Text="{x:Bind ViewModel.Instruction, Mode=TwoWay}"
Text="{x:Bind ViewModel.Instruction, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
Height="256" />
</StackPanel>

<ScrollView
<TextBlock
Grid.Row="2"
Grid.ColumnSpan="2">
<StackPanel>
Grid.ColumnSpan="2"
Style="{StaticResource SubtitleTextBlockStyle}"
Text="出力された物語"
Margin="{StaticResource SmallTopMargin}" />

<ScrollView
Grid.Row="3"
Grid.ColumnSpan="2"
Background="{ThemeResource CardBackgroundFillColorDefault}"
BorderBrush="{ThemeResource CardStrokeColorDefault}"
BorderThickness="1"
CornerRadius="12">
<StackPanel Padding="16,12,16,24">
<TextBlock
Style="{StaticResource SubtitleTextBlockStyle}"
Text="出力された物語"
Margin="{StaticResource SmallTopMargin}" />

Style="{StaticResource SubtitleTextBlockStyle}"
Text="{x:Bind ViewModel.AiStoryTitle, Mode=OneWay}" />
<TextBlock
x:Name="StoryContnent"
Margin="{StaticResource XSmallTopMargin}"
TextWrapping="Wrap"/>
</StackPanel>
</ScrollView>
</Grid>
Expand Down
33 changes: 31 additions & 2 deletions KoeBook/Views/CreateStoryPage.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
using KoeBook.ViewModels;
using System.Diagnostics;
using KoeBook.Models;
using KoeBook.ViewModels;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Documents;

// To learn more about WinUI, the WinUI project structure,
// and more about our project templates, see: http://aka.ms/winui-project-info.
Expand All @@ -18,7 +21,33 @@ public sealed partial class CreateStoryPage : Page
public CreateStoryPage()
{
ViewModel = App.GetService<CreateStoryViewModel>();
ViewModel.PropertyChanged += ViewModel_PropertyChanged;

this.InitializeComponent();
InitializeComponent();
}

private void ViewModel_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
if (e.PropertyName != nameof(ViewModel.AiStory))
return;

StoryContnent.Inlines.Clear();
if (ViewModel.AiStory is null)
return;

foreach (var inline in ViewModel.AiStory.Lines
.SelectMany(l =>
l.SelectMany(l =>
l.Inlines.Select(Inline (inline) => inline switch
{
AiStory.TextElement text => new Run() { Text = text.InnerText },
AiStory.Ruby ruby => new Run() { Text = ruby.Rt },
_ => throw new UnreachableException(),
}).Append(new LineBreak())
).Append(new LineBreak())
).SkipLast(2))
{
StoryContnent.Inlines.Add(inline);
}
}
}
10 changes: 4 additions & 6 deletions KoeBook/Views/ShellPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@
x:Class="KoeBook.Views.ShellPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="using:KoeBook.Views"
xmlns:helpers="using:KoeBook.Helpers"
xmlns:behaviors="using:KoeBook.Behaviors"
xmlns:i="using:Microsoft.Xaml.Interactivity"
Loaded="OnLoaded">

<Grid>
Expand All @@ -28,7 +23,10 @@
<TabViewItem
x:Name="CreateStoryPageTab"
IsClosable="False"
Header="作成">
Header="AIストーリー">
<TabViewItem.IconSource>
<SymbolIconSource Symbol="Add" />
</TabViewItem.IconSource>
<local:CreateStoryPage Margin="20,10,20,10" />
</TabViewItem>
</TabView.TabItems>
Expand Down
3 changes: 2 additions & 1 deletion KoeBook/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"IAnalyzerService": false,
"IEpubGenerateService": false,
"ISoundGenerationSelectorService": false,
"ISoundGenerationService": false
"ISoundGenerationService": false,
"IStoryCreaterService": false
}
}

0 comments on commit cb6de09

Please sign in to comment.