This commit is contained in:
2026-05-08 11:44:42 +08:00
parent b08127031c
commit abf1f1ebea
249 changed files with 39411 additions and 887 deletions

View File

@@ -0,0 +1,390 @@
//! 视觉小说模板的纯领域模型。
//!
//! 本 crate 只负责草稿校验、运行时 step 解析、状态推进、历史重生成边界
//! 和平台统一存档状态构造HTTP、SpacetimeDB、LLM、OSS 均由外层 adapter 处理。
use serde::{Deserialize, Serialize};
#[cfg(feature = "spacetime-types")]
use spacetimedb::SpacetimeType;
use std::collections::BTreeMap;
pub const VISUAL_NOVEL_PROFILE_ID_PREFIX: &str = "vn-profile-";
pub const VISUAL_NOVEL_RUN_ID_PREFIX: &str = "vn-run-";
pub const VISUAL_NOVEL_HISTORY_ID_PREFIX: &str = "vn-history-";
pub const VISUAL_NOVEL_DEFAULT_MAX_HISTORY_ENTRIES: u32 = 80;
pub const VISUAL_NOVEL_DEFAULT_MAX_ASSISTANT_STEP_COUNT_PER_TURN: u32 = 8;
pub const VISUAL_NOVEL_MIN_INITIAL_CHOICE_COUNT: usize = 2;
pub const VISUAL_NOVEL_MAX_INITIAL_CHOICE_COUNT: usize = 4;
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelSourceMode {
Idea,
Document,
Blank,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisualNovelCharacterRole {
Protagonist,
Main,
Supporting,
Antagonist,
Background,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisualNovelAssetSource {
PlatformAsset,
Generated,
External,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisualNovelSceneAvailability {
Opening,
Always,
PhaseLocked,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisualNovelAttributePanelMode {
Off,
PlatformWhitelist,
TemplateConfig,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelValidationSeverity {
Error,
Warning,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelRunMode {
Test,
Play,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelRunStatus {
Active,
Completed,
Failed,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum VisualNovelRuntimeActionKind {
Choice,
FreeText,
Continue,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelTransitionKind {
Fade,
Cut,
Flash,
None,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum VisualNovelHistorySource {
Player,
Assistant,
System,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum VisualNovelFlagValue {
String(String),
Number(f64),
Bool(bool),
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelValidationIssue {
pub issue_id: String,
pub code: String,
pub severity: VisualNovelValidationSeverity,
pub path: String,
pub message: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelChoiceDraft {
pub choice_id: String,
pub text: String,
pub action_hint: Option<String>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelCharacterImageAsset {
pub asset_id: String,
pub image_src: String,
pub expression: Option<String>,
pub source: VisualNovelAssetSource,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelWorldDraft {
pub title: String,
pub summary: String,
pub background: String,
pub premise: String,
pub literary_style: String,
pub player_role: String,
pub default_tone: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelCharacterDraft {
pub character_id: String,
pub name: String,
pub gender: Option<String>,
pub role: VisualNovelCharacterRole,
pub appearance: String,
pub personality: String,
pub tone: String,
pub background: String,
pub relationship_to_player: String,
pub image_assets: Vec<VisualNovelCharacterImageAsset>,
pub default_expression: Option<String>,
pub is_player_visible: bool,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelSceneDraft {
pub scene_id: String,
pub name: String,
pub description: String,
pub background_image_src: Option<String>,
pub music_src: Option<String>,
pub ambient_sound_src: Option<String>,
pub availability: VisualNovelSceneAvailability,
pub phase_ids: Vec<String>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelStoryPhaseDraft {
pub phase_id: String,
pub title: String,
pub goal: String,
pub summary: String,
pub entry_condition: String,
pub exit_condition: String,
pub scene_ids: Vec<String>,
pub character_ids: Vec<String>,
pub suggested_choices: Vec<String>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelOpeningDraft {
pub scene_id: Option<String>,
pub narration: String,
pub speaker_character_id: Option<String>,
pub first_dialogue: Option<String>,
pub initial_choices: Vec<VisualNovelChoiceDraft>,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelRuntimeConfigDraft {
pub text_mode_enabled: bool,
pub default_text_mode: bool,
pub max_history_entries: u32,
pub max_assistant_step_count_per_turn: u32,
pub allow_free_text_action: bool,
pub allow_history_regeneration: bool,
pub attribute_panel_mode: VisualNovelAttributePanelMode,
pub save_archive_enabled: bool,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelResultDraft {
pub profile_id: Option<String>,
pub work_title: String,
pub work_description: String,
pub work_tags: Vec<String>,
pub cover_image_src: Option<String>,
pub source_mode: VisualNovelSourceMode,
pub source_asset_ids: Vec<String>,
pub world: VisualNovelWorldDraft,
pub characters: Vec<VisualNovelCharacterDraft>,
pub scenes: Vec<VisualNovelSceneDraft>,
pub story_phases: Vec<VisualNovelStoryPhaseDraft>,
pub opening: VisualNovelOpeningDraft,
pub runtime_config: VisualNovelRuntimeConfigDraft,
pub publish_ready: bool,
pub validation_issues: Vec<VisualNovelValidationIssue>,
pub updated_at: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelWorkProfile {
pub profile_id: String,
pub work_title: String,
pub work_description: String,
pub work_tags: Vec<String>,
pub cover_image_src: Option<String>,
pub source_mode: VisualNovelSourceMode,
pub draft: VisualNovelResultDraft,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(
tag = "type",
rename_all = "snake_case",
rename_all_fields = "camelCase"
)]
pub enum VisualNovelRuntimeStep {
SceneChange {
scene_id: String,
background_image_src: Option<String>,
music_src: Option<String>,
},
Narration {
text: String,
},
Dialogue {
character_id: String,
character_name: String,
expression: Option<String>,
text: String,
},
Transition {
transition_kind: VisualNovelTransitionKind,
text: Option<String>,
},
Choice {
choices: Vec<VisualNovelChoiceDraft>,
},
Flag {
key: String,
value: VisualNovelFlagValue,
},
Metric {
key: String,
delta: f64,
},
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelHistoryEntry {
pub entry_id: String,
pub run_id: String,
pub turn_index: u32,
pub source: VisualNovelHistorySource,
pub action_text: Option<String>,
pub steps: Vec<VisualNovelRuntimeStep>,
pub snapshot_before_hash: Option<String>,
pub snapshot_after_hash: Option<String>,
pub created_at: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelRunSnapshot {
pub run_id: String,
pub owner_user_id: String,
pub profile_id: String,
pub mode: VisualNovelRunMode,
pub status: VisualNovelRunStatus,
pub current_scene_id: Option<String>,
pub current_phase_id: Option<String>,
pub visible_character_ids: Vec<String>,
pub flags: BTreeMap<String, VisualNovelFlagValue>,
pub metrics: BTreeMap<String, f64>,
pub history: Vec<VisualNovelHistoryEntry>,
pub available_choices: Vec<VisualNovelChoiceDraft>,
pub text_mode_enabled: bool,
pub created_at: String,
pub updated_at: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelRuntimeAction {
pub action_kind: VisualNovelRuntimeActionKind,
pub choice_id: Option<String>,
pub text: Option<String>,
pub client_event_id: String,
}
#[cfg_attr(feature = "spacetime-types", derive(SpacetimeType))]
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct VisualNovelSaveArchiveState {
pub runtime_kind: String,
pub profile_id: String,
pub run_id: String,
pub current_scene_id: Option<String>,
pub current_phase_id: Option<String>,
pub history_cursor: u32,
pub snapshot_hash: Option<String>,
}
impl Default for VisualNovelRuntimeConfigDraft {
fn default() -> Self {
Self {
text_mode_enabled: true,
default_text_mode: false,
max_history_entries: VISUAL_NOVEL_DEFAULT_MAX_HISTORY_ENTRIES,
max_assistant_step_count_per_turn:
VISUAL_NOVEL_DEFAULT_MAX_ASSISTANT_STEP_COUNT_PER_TURN,
allow_free_text_action: true,
allow_history_regeneration: true,
attribute_panel_mode: VisualNovelAttributePanelMode::Off,
save_archive_enabled: true,
}
}
}