Store kind "strings": string packs install like themes and plugins (dropped .strings.json, links, the signed Store), validated as plain data on this side (envelope, plain text, size caps, the app's own site), kept in app data strings/ with their order and which are on (at most 8); licence and credits are optional fields of every catalog item; five shell-only commands (strings_list, strings_read, strings_set, strings_remove, strings_apply); the shell's delivery (packs + the inventory slice) goes into site tabs' session storage by revision, as the first document-start layer of a site tab and never in safe mode
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
8e9442c2fc
commit
419f60b5b1
10 changed files with 593 additions and 13 deletions
|
|
@ -59,6 +59,8 @@ pub struct StoreConfig {
|
|||
pub enum Kind {
|
||||
Theme,
|
||||
Plugin,
|
||||
/// A string pack: data that replaces the site's own UI strings (`store::strings`).
|
||||
Strings,
|
||||
}
|
||||
|
||||
impl Kind {
|
||||
|
|
@ -66,7 +68,8 @@ impl Kind {
|
|||
match value {
|
||||
"theme" => Ok(Kind::Theme),
|
||||
"plugin" => Ok(Kind::Plugin),
|
||||
_ => Err("VALIDATION: kind must be \"theme\" or \"plugin\"".into()),
|
||||
"strings" => Ok(Kind::Strings),
|
||||
_ => Err("VALIDATION: kind must be \"theme\", \"plugin\" or \"strings\"".into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,6 +77,7 @@ impl Kind {
|
|||
match self {
|
||||
Kind::Theme => "theme",
|
||||
Kind::Plugin => "plugin",
|
||||
Kind::Strings => "strings",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,6 +85,7 @@ impl Kind {
|
|||
match self {
|
||||
Kind::Theme => MAX_THEME_BYTES,
|
||||
Kind::Plugin => MAX_PLUGIN_BYTES as usize,
|
||||
Kind::Strings => super::strings::MAX_STRINGS_BYTES,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -288,6 +293,15 @@ pub struct CatalogItem {
|
|||
/// Themes: a few colors for swatches.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub colors: Option<Swatch>,
|
||||
/// String packs: the language tag of the pack.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub language: Option<String>,
|
||||
/// String packs: the licence of the text (an SPDX id such as `CC-BY-4.0`, or a short name).
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub license: Option<String>,
|
||||
/// String packs: who translated and reviewed it, plain text.
|
||||
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||
pub credits: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||
|
|
@ -360,6 +374,13 @@ impl CatalogItem {
|
|||
if self.size == 0 || self.size > self.kind.max_bytes() as u64 {
|
||||
return bad("size");
|
||||
}
|
||||
// Any item may name its licence and credit its authors; only a string pack has a language.
|
||||
if !self.license.as_deref().is_none_or(|v| !v.trim().is_empty() && valid_text(v, 60)) || !self.credits.as_deref().is_none_or(|v| valid_text(v, 600)) {
|
||||
return bad("license or credits");
|
||||
}
|
||||
if self.kind != Kind::Strings && self.language.is_some() {
|
||||
return bad("language");
|
||||
}
|
||||
match self.kind {
|
||||
Kind::Plugin => {
|
||||
if !self.file.as_deref().is_some_and(valid_file_name) || self.colors.is_some() {
|
||||
|
|
@ -372,6 +393,14 @@ impl CatalogItem {
|
|||
}
|
||||
}
|
||||
}
|
||||
Kind::Strings => {
|
||||
if self.file.is_some() || self.permissions.is_some() || self.colors.is_some() {
|
||||
return bad("string pack fields");
|
||||
}
|
||||
if !self.language.as_deref().is_some_and(super::strings::valid_language) {
|
||||
return bad("language");
|
||||
}
|
||||
}
|
||||
Kind::Theme => {
|
||||
if self.file.is_some() || self.permissions.is_some() {
|
||||
return bad("theme fields");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue