Phase C3: Character & Asset Lane Storytelling Improvements
This commit is contained in:
parent
d0939b914a
commit
36c214fab8
@ -151,6 +151,21 @@
|
|||||||
}
|
}
|
||||||
return string.Join(Environment.NewLine, lines);
|
return string.Join(Environment.NewLine, lines);
|
||||||
}
|
}
|
||||||
|
string TrimMarkerLabel(string value)
|
||||||
|
=> string.IsNullOrWhiteSpace(value) ? string.Empty : value.Trim().Length <= 5 ? value.Trim() : value.Trim()[..5];
|
||||||
|
IReadOnlyList<ContinuityWarning> EntityWarnings(Scene scene, string entityType, int entityId)
|
||||||
|
=> Model.WarningsByScene.TryGetValue(scene.SceneID, out var sceneWarnings)
|
||||||
|
? sceneWarnings
|
||||||
|
.Where(warning => string.Equals(warning.EntityType, entityType, StringComparison.OrdinalIgnoreCase)
|
||||||
|
&& warning.EntityID == entityId)
|
||||||
|
.OrderByDescending(warning => warning.SeverityName)
|
||||||
|
.ThenBy(warning => warning.WarningTypeName)
|
||||||
|
.ToList()
|
||||||
|
: [];
|
||||||
|
bool HasStoryState(SceneCharacter appearance)
|
||||||
|
=> !string.IsNullOrWhiteSpace(appearance.EmotionalState)
|
||||||
|
|| !string.IsNullOrWhiteSpace(appearance.PhysicalCondition)
|
||||||
|
|| !string.IsNullOrWhiteSpace(appearance.KnowledgeNotes);
|
||||||
string AssetMarkerLabel(AssetEvent assetEvent)
|
string AssetMarkerLabel(AssetEvent assetEvent)
|
||||||
{
|
{
|
||||||
var typeName = assetEvent.AssetEventTypeName?.Trim() ?? string.Empty;
|
var typeName = assetEvent.AssetEventTypeName?.Trim() ?? string.Empty;
|
||||||
@ -172,7 +187,7 @@
|
|||||||
"investigated" => "?",
|
"investigated" => "?",
|
||||||
"resolved" => "Done",
|
"resolved" => "Done",
|
||||||
"state changed" => "State",
|
"state changed" => "State",
|
||||||
_ => string.IsNullOrWhiteSpace(typeName) ? "Asset" : typeName.Length <= 5 ? typeName : typeName[..5]
|
_ => string.IsNullOrWhiteSpace(typeName) ? "Asset" : TrimMarkerLabel(typeName)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
string AssetMarkerMeaning(AssetEvent assetEvent)
|
string AssetMarkerMeaning(AssetEvent assetEvent)
|
||||||
@ -199,27 +214,96 @@
|
|||||||
_ => string.IsNullOrWhiteSpace(typeName) ? "Asset event occurs in this scene." : $"Asset event: {typeName}."
|
_ => string.IsNullOrWhiteSpace(typeName) ? "Asset event occurs in this scene." : $"Asset event: {typeName}."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
string AssetMarkerTooltip(AssetEvent assetEvent, Scene scene)
|
string AssetStorySentence(AssetEvent assetEvent)
|
||||||
|
{
|
||||||
|
var title = assetEvent.EventTitle?.Trim();
|
||||||
|
if (!string.IsNullOrWhiteSpace(title) && !string.Equals(title, assetEvent.AssetEventTypeName, StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return $"{assetEvent.AssetName}: {title}.";
|
||||||
|
}
|
||||||
|
|
||||||
|
return AssetMarkerMeaning(assetEvent);
|
||||||
|
}
|
||||||
|
string AssetMarkerClass(AssetEvent assetEvent, StoryAsset asset, IReadOnlyList<ContinuityWarning> assetWarnings)
|
||||||
|
{
|
||||||
|
var classes = new List<string>();
|
||||||
|
var typeName = assetEvent.AssetEventTypeName?.Trim().ToLowerInvariant() ?? string.Empty;
|
||||||
|
if (assetWarnings.Any())
|
||||||
|
{
|
||||||
|
classes.Add("timeline-marker-has-warning");
|
||||||
|
}
|
||||||
|
if (asset.Importance >= 8)
|
||||||
|
{
|
||||||
|
classes.Add("timeline-marker-important");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(assetEvent.ToStateName))
|
||||||
|
{
|
||||||
|
classes.Add("timeline-marker-state-change");
|
||||||
|
}
|
||||||
|
if (typeName is "found" or "revealed" or "destroyed" or "resolved" or "state changed")
|
||||||
|
{
|
||||||
|
classes.Add("timeline-marker-story-turn");
|
||||||
|
}
|
||||||
|
if (typeName is "given" or "taken" or "stolen" or "moved" or "location changed")
|
||||||
|
{
|
||||||
|
classes.Add("timeline-marker-transfer");
|
||||||
|
}
|
||||||
|
return string.Join(' ', classes);
|
||||||
|
}
|
||||||
|
string AssetMarkerTooltip(AssetEvent assetEvent, StoryAsset asset, Scene scene, IReadOnlyList<ContinuityWarning> assetWarnings)
|
||||||
{
|
{
|
||||||
var lines = new List<string>
|
var lines = new List<string>
|
||||||
{
|
{
|
||||||
AssetMarkerMeaning(assetEvent),
|
AssetStorySentence(assetEvent),
|
||||||
$"Asset: {assetEvent.AssetName}",
|
$"Asset: {assetEvent.AssetName}",
|
||||||
$"Event: {assetEvent.EventTitle}",
|
$"Event type: {(string.IsNullOrWhiteSpace(assetEvent.AssetEventTypeName) ? "Not set" : assetEvent.AssetEventTypeName)}",
|
||||||
$"Scene: {scene.SceneNumber:g}: {scene.SceneTitle}"
|
$"Scene: {scene.SceneNumber:g}: {scene.SceneTitle}"
|
||||||
};
|
};
|
||||||
|
if (asset.Importance >= 8)
|
||||||
|
{
|
||||||
|
lines.Add("Significance: High-importance story asset.");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(assetEvent.FromStateName) || !string.IsNullOrWhiteSpace(assetEvent.ToStateName))
|
||||||
|
{
|
||||||
|
lines.Add($"State change: {(string.IsNullOrWhiteSpace(assetEvent.FromStateName) ? "Unknown" : assetEvent.FromStateName)} -> {(string.IsNullOrWhiteSpace(assetEvent.ToStateName) ? "Unknown" : assetEvent.ToStateName)}");
|
||||||
|
}
|
||||||
if (!string.IsNullOrWhiteSpace(assetEvent.ToStateName))
|
if (!string.IsNullOrWhiteSpace(assetEvent.ToStateName))
|
||||||
{
|
{
|
||||||
lines.Add($"State changes to: {assetEvent.ToStateName}");
|
lines.Add($"Current state after this event: {assetEvent.ToStateName}");
|
||||||
}
|
}
|
||||||
if (!string.IsNullOrWhiteSpace(assetEvent.EventDescription))
|
if (!string.IsNullOrWhiteSpace(assetEvent.EventDescription))
|
||||||
{
|
{
|
||||||
lines.Add($"Summary: {assetEvent.EventDescription}");
|
lines.Add($"Summary: {assetEvent.EventDescription}");
|
||||||
}
|
}
|
||||||
|
foreach (var warning in assetWarnings.Take(2))
|
||||||
|
{
|
||||||
|
lines.Add($"Continuity: {warning.WarningTypeName} - {(string.IsNullOrWhiteSpace(warning.Details) ? warning.Message : warning.Details)}");
|
||||||
|
}
|
||||||
return string.Join(Environment.NewLine, lines);
|
return string.Join(Environment.NewLine, lines);
|
||||||
}
|
}
|
||||||
string CharacterMarkerLabel(SceneCharacter appearance)
|
string CharacterMarkerLabel(SceneCharacter appearance, SceneCharacter? firstAppearance, SceneCharacter? lastAppearance)
|
||||||
{
|
{
|
||||||
|
if (firstAppearance?.SceneCharacterID == appearance.SceneCharacterID)
|
||||||
|
{
|
||||||
|
return "Intro";
|
||||||
|
}
|
||||||
|
if (lastAppearance?.SceneCharacterID == appearance.SceneCharacterID
|
||||||
|
&& string.Equals(appearance.PresenceTypeName, "Leaves", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return "Exit";
|
||||||
|
}
|
||||||
|
if (string.Equals(appearance.RoleInSceneTypeName, "POV Character", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return "POV";
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(appearance.KnowledgeNotes))
|
||||||
|
{
|
||||||
|
return "Know";
|
||||||
|
}
|
||||||
|
if (HasStoryState(appearance))
|
||||||
|
{
|
||||||
|
return "State";
|
||||||
|
}
|
||||||
var presence = appearance.PresenceTypeName?.Trim() ?? string.Empty;
|
var presence = appearance.PresenceTypeName?.Trim() ?? string.Empty;
|
||||||
return presence.ToLowerInvariant() switch
|
return presence.ToLowerInvariant() switch
|
||||||
{
|
{
|
||||||
@ -235,8 +319,25 @@
|
|||||||
_ => "Char"
|
_ => "Char"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
string CharacterMarkerMeaning(SceneCharacter appearance)
|
string CharacterMarkerMeaning(SceneCharacter appearance, SceneCharacter? firstAppearance, SceneCharacter? lastAppearance)
|
||||||
{
|
{
|
||||||
|
if (firstAppearance?.SceneCharacterID == appearance.SceneCharacterID)
|
||||||
|
{
|
||||||
|
return $"{appearance.CharacterName} enters this story lane in this scene.";
|
||||||
|
}
|
||||||
|
if (lastAppearance?.SceneCharacterID == appearance.SceneCharacterID
|
||||||
|
&& string.Equals(appearance.PresenceTypeName, "Leaves", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return $"{appearance.CharacterName} exits this story lane in this scene.";
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(appearance.KnowledgeNotes))
|
||||||
|
{
|
||||||
|
return $"{appearance.CharacterName}'s knowledge changes in this scene.";
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(appearance.EmotionalState) || !string.IsNullOrWhiteSpace(appearance.PhysicalCondition))
|
||||||
|
{
|
||||||
|
return $"{appearance.CharacterName}'s story state changes in this scene.";
|
||||||
|
}
|
||||||
var presence = appearance.PresenceTypeName?.Trim() ?? string.Empty;
|
var presence = appearance.PresenceTypeName?.Trim() ?? string.Empty;
|
||||||
return presence.ToLowerInvariant() switch
|
return presence.ToLowerInvariant() switch
|
||||||
{
|
{
|
||||||
@ -252,16 +353,75 @@
|
|||||||
_ => "Character appears in this scene."
|
_ => "Character appears in this scene."
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
string CharacterMarkerTooltip(SceneCharacter appearance, Scene scene)
|
string CharacterMarkerClass(SceneCharacter appearance, SceneCharacter? firstAppearance, SceneCharacter? lastAppearance, IReadOnlyList<ContinuityWarning> characterWarnings)
|
||||||
|
{
|
||||||
|
var classes = new List<string>();
|
||||||
|
if (characterWarnings.Any())
|
||||||
|
{
|
||||||
|
classes.Add("timeline-marker-has-warning");
|
||||||
|
}
|
||||||
|
if (firstAppearance?.SceneCharacterID == appearance.SceneCharacterID)
|
||||||
|
{
|
||||||
|
classes.Add("timeline-marker-story-turn");
|
||||||
|
}
|
||||||
|
if (lastAppearance?.SceneCharacterID == appearance.SceneCharacterID
|
||||||
|
&& string.Equals(appearance.PresenceTypeName, "Leaves", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
classes.Add("timeline-marker-exit");
|
||||||
|
}
|
||||||
|
if (string.Equals(appearance.RoleInSceneTypeName, "POV Character", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(appearance.RoleInSceneTypeName, "Main Participant", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
classes.Add("timeline-marker-important");
|
||||||
|
}
|
||||||
|
if (HasStoryState(appearance))
|
||||||
|
{
|
||||||
|
classes.Add("timeline-marker-state-change");
|
||||||
|
}
|
||||||
|
return string.Join(' ', classes);
|
||||||
|
}
|
||||||
|
string CharacterMarkerTooltip(SceneCharacter appearance, Scene scene, SceneCharacter? firstAppearance, SceneCharacter? lastAppearance, IReadOnlyList<ContinuityWarning> characterWarnings)
|
||||||
{
|
{
|
||||||
var lines = new List<string>
|
var lines = new List<string>
|
||||||
{
|
{
|
||||||
CharacterMarkerMeaning(appearance),
|
CharacterMarkerMeaning(appearance, firstAppearance, lastAppearance),
|
||||||
$"Character: {appearance.CharacterName}",
|
$"Character: {appearance.CharacterName}",
|
||||||
$"Role: {(string.IsNullOrWhiteSpace(appearance.RoleInSceneTypeName) ? "Not set" : appearance.RoleInSceneTypeName)}",
|
$"Role: {(string.IsNullOrWhiteSpace(appearance.RoleInSceneTypeName) ? "Not set" : appearance.RoleInSceneTypeName)}",
|
||||||
$"Presence: {(string.IsNullOrWhiteSpace(appearance.PresenceTypeName) ? "Not set" : appearance.PresenceTypeName)}",
|
$"Presence: {(string.IsNullOrWhiteSpace(appearance.PresenceTypeName) ? "Not set" : appearance.PresenceTypeName)}",
|
||||||
$"Scene: {scene.SceneNumber:g}: {scene.SceneTitle}"
|
$"Scene: {scene.SceneNumber:g}: {scene.SceneTitle}"
|
||||||
};
|
};
|
||||||
|
if (!string.IsNullOrWhiteSpace(appearance.LocationPath) || !string.IsNullOrWhiteSpace(appearance.LocationName))
|
||||||
|
{
|
||||||
|
lines.Add($"Location: {appearance.LocationPath ?? appearance.LocationName}");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(appearance.EntryLocationName))
|
||||||
|
{
|
||||||
|
lines.Add($"Entry: {appearance.EntryLocationName}");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(appearance.ExitLocationName))
|
||||||
|
{
|
||||||
|
lines.Add($"Exit: {appearance.ExitLocationName}");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(appearance.EmotionalState))
|
||||||
|
{
|
||||||
|
lines.Add($"Emotional state: {appearance.EmotionalState}");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(appearance.PhysicalCondition))
|
||||||
|
{
|
||||||
|
lines.Add($"Physical condition: {appearance.PhysicalCondition}");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(appearance.KnowledgeNotes))
|
||||||
|
{
|
||||||
|
lines.Add($"Knowledge: {appearance.KnowledgeNotes}");
|
||||||
|
}
|
||||||
|
if (!string.IsNullOrWhiteSpace(appearance.AppearanceNotes))
|
||||||
|
{
|
||||||
|
lines.Add($"Summary: {appearance.AppearanceNotes}");
|
||||||
|
}
|
||||||
|
foreach (var warning in characterWarnings.Take(2))
|
||||||
|
{
|
||||||
|
lines.Add($"Continuity: {warning.WarningTypeName} - {(string.IsNullOrWhiteSpace(warning.Details) ? warning.Message : warning.Details)}");
|
||||||
|
}
|
||||||
return string.Join(Environment.NewLine, lines);
|
return string.Join(Environment.NewLine, lines);
|
||||||
}
|
}
|
||||||
bool HasLeadSceneRole(CharacterLaneViewModel lane) => lane.SceneSlots
|
bool HasLeadSceneRole(CharacterLaneViewModel lane) => lane.SceneSlots
|
||||||
@ -1169,8 +1329,10 @@ else
|
|||||||
<div class="asset-lane-slot" title="Scene @realAssetScene.SceneNumber: @realAssetScene.SceneTitle">
|
<div class="asset-lane-slot" title="Scene @realAssetScene.SceneNumber: @realAssetScene.SceneTitle">
|
||||||
@foreach (var assetEvent in slot?.Events ?? Enumerable.Empty<AssetEvent>())
|
@foreach (var assetEvent in slot?.Events ?? Enumerable.Empty<AssetEvent>())
|
||||||
{
|
{
|
||||||
var assetMarkerTooltip = AssetMarkerTooltip(assetEvent, realAssetScene);
|
var assetWarnings = EntityWarnings(realAssetScene, "StoryAsset", assetEvent.StoryAssetID);
|
||||||
<a class="asset-marker"
|
var assetMarkerTooltip = AssetMarkerTooltip(assetEvent, lane.Asset, realAssetScene, assetWarnings);
|
||||||
|
var assetMarkerClass = AssetMarkerClass(assetEvent, lane.Asset, assetWarnings);
|
||||||
|
<a class="asset-marker @assetMarkerClass"
|
||||||
asp-controller="Timeline"
|
asp-controller="Timeline"
|
||||||
asp-action="Index"
|
asp-action="Index"
|
||||||
asp-route-ProjectID="@Model.Project.ProjectID"
|
asp-route-ProjectID="@Model.Project.ProjectID"
|
||||||
@ -1226,6 +1388,13 @@ else
|
|||||||
{
|
{
|
||||||
var lane = laneItem.lane;
|
var lane = laneItem.lane;
|
||||||
var characterFocusClass = Model.Filter.FocusType == "character" && Model.Filter.FocusID.HasValue && Model.Filter.FocusID != lane.Character.CharacterID ? "lane-dim" : "";
|
var characterFocusClass = Model.Filter.FocusType == "character" && Model.Filter.FocusID.HasValue && Model.Filter.FocusID != lane.Character.CharacterID ? "lane-dim" : "";
|
||||||
|
var orderedLaneAppearances = lane.SceneSlots
|
||||||
|
.SelectMany(slot => slot.Appearances)
|
||||||
|
.OrderBy(appearance => timelineColumns.FindIndex(column => column.Scene?.SceneID == appearance.SceneID))
|
||||||
|
.ThenBy(appearance => appearance.SceneCharacterID)
|
||||||
|
.ToList();
|
||||||
|
var firstAppearance = orderedLaneAppearances.FirstOrDefault();
|
||||||
|
var lastAppearance = orderedLaneAppearances.LastOrDefault();
|
||||||
<div class="character-lane-row @characterFocusClass">
|
<div class="character-lane-row @characterFocusClass">
|
||||||
<a class="character-lane-label" asp-controller="Characters" asp-action="Details" asp-route-id="@lane.Character.CharacterID">
|
<a class="character-lane-label" asp-controller="Characters" asp-action="Details" asp-route-id="@lane.Character.CharacterID">
|
||||||
<strong>@lane.Character.CharacterName</strong>
|
<strong>@lane.Character.CharacterName</strong>
|
||||||
@ -1244,8 +1413,10 @@ else
|
|||||||
<div class="character-lane-slot" title="Scene @realCharacterScene.SceneNumber: @realCharacterScene.SceneTitle">
|
<div class="character-lane-slot" title="Scene @realCharacterScene.SceneNumber: @realCharacterScene.SceneTitle">
|
||||||
@foreach (var appearance in slot?.Appearances ?? Enumerable.Empty<SceneCharacter>())
|
@foreach (var appearance in slot?.Appearances ?? Enumerable.Empty<SceneCharacter>())
|
||||||
{
|
{
|
||||||
var characterMarkerTooltip = CharacterMarkerTooltip(appearance, realCharacterScene);
|
var characterWarnings = EntityWarnings(realCharacterScene, "Character", appearance.CharacterID);
|
||||||
<a class="character-marker"
|
var characterMarkerTooltip = CharacterMarkerTooltip(appearance, realCharacterScene, firstAppearance, lastAppearance, characterWarnings);
|
||||||
|
var characterMarkerClass = CharacterMarkerClass(appearance, firstAppearance, lastAppearance, characterWarnings);
|
||||||
|
<a class="character-marker @characterMarkerClass"
|
||||||
asp-controller="Timeline"
|
asp-controller="Timeline"
|
||||||
asp-action="Index"
|
asp-action="Index"
|
||||||
asp-route-ProjectID="@Model.Project.ProjectID"
|
asp-route-ProjectID="@Model.Project.ProjectID"
|
||||||
@ -1255,7 +1426,7 @@ else
|
|||||||
asp-route-FocusID="@appearance.CharacterID"
|
asp-route-FocusID="@appearance.CharacterID"
|
||||||
title="@characterMarkerTooltip"
|
title="@characterMarkerTooltip"
|
||||||
aria-label="@characterMarkerTooltip">
|
aria-label="@characterMarkerTooltip">
|
||||||
@CharacterMarkerLabel(appearance)
|
@CharacterMarkerLabel(appearance, firstAppearance, lastAppearance)
|
||||||
</a>
|
</a>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -16,5 +16,11 @@
|
|||||||
"inputFiles": [
|
"inputFiles": [
|
||||||
"wwwroot/js/site.js"
|
"wwwroot/js/site.js"
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"outputFileName": "wwwroot/js/word-companion-host.min.js",
|
||||||
|
"inputFiles": [
|
||||||
|
"wwwroot/js/word-companion-host.js"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|||||||
@ -1454,6 +1454,35 @@ body.timeline-inspector-resizing {
|
|||||||
background: #dfd6f7;
|
background: #dfd6f7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.asset-marker.timeline-marker-story-turn,
|
||||||
|
.character-marker.timeline-marker-story-turn {
|
||||||
|
border-width: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-marker.timeline-marker-important,
|
||||||
|
.character-marker.timeline-marker-important {
|
||||||
|
box-shadow: inset 0 -3px 0 rgba(197, 139, 43, 0.36);
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-marker.timeline-marker-state-change,
|
||||||
|
.character-marker.timeline-marker-state-change {
|
||||||
|
border-style: double;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-marker.timeline-marker-transfer {
|
||||||
|
box-shadow: inset 3px 0 0 rgba(47, 111, 99, 0.32);
|
||||||
|
}
|
||||||
|
|
||||||
|
.character-marker.timeline-marker-exit {
|
||||||
|
opacity: 0.86;
|
||||||
|
border-style: dashed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asset-marker.timeline-marker-has-warning,
|
||||||
|
.character-marker.timeline-marker-has-warning {
|
||||||
|
box-shadow: 0 0 0 2px rgba(179, 95, 59, 0.22);
|
||||||
|
}
|
||||||
|
|
||||||
.asset-events-section {
|
.asset-events-section {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
}
|
}
|
||||||
|
|||||||
25
PlotLine/wwwroot/js/word-companion-host.min.js
vendored
Normal file
25
PlotLine/wwwroot/js/word-companion-host.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
x
Reference in New Issue
Block a user