feat(domain,application,infrastructure,backend,web-server,app-tauri,frontend): bibliothèque d'icônes projet personnalisées importées par l'utilisateur — #358 (QA verte)
domain: variante ProjectIconSet::Custom, métadonnées CustomProjectIconAsset (id opaque, media type png/jpeg/webp, byte_size, dimensions décodées, sha256, nom de fichier original sans chemin, created_at), CustomProjectIconContent et port CustomProjectIconAssetStore (list/read/exists/create/delete) avec CustomProjectIconStoreError dédiée. application: use case project_icon_assets — import validé png/jpeg/webp avec rejet du contenu SVG, dimensionnement et digest sha256, bibliothèque partagée à l'instance indépendante de tout projet, suppression refusée tant que l'asset est référencé par un projet. infrastructure: store fichier store/project_icon_asset.rs — index canonique + octets, création atomique sous extension validée. backend/web-server: contrat HTTP /api/project-icon-assets (GET liste, POST import, GET lecture, GET /usage, DELETE) avec prévol OPTIONS et tests contrat incluant le rejet SVG. app-tauri: commandes import/list/read/get_usage/delete_custom_project_icon_asset exposées au frontend. frontend: port ProjectIconAssetGateway (list/import/delete/getUrl sans exposer de chemin filesystem), gateways http+mock avec tests, DI, section Icônes personnalisées du ProjectIconPicker (import fichier accept png/jpeg/webp, sélection role=option, suppression avec retrait de sélection, erreurs lisibles i18n FR/EN), tests projectIcons étendus. QA verte: contrats HTTP et Tauri identiques, suites cargo et npm ciblées vertes.
This commit is contained in:
@ -270,8 +270,9 @@ pub use scheduled_task::{
|
||||
};
|
||||
|
||||
pub use settings::{
|
||||
DefaultAgentTerminalInterface, IdeaLanguage, IdeaUserSettings, ProjectIconRef, ProjectIconSet,
|
||||
ProjectSettings, ThemeColor, MAX_PROJECT_ICON_NAME_LEN,
|
||||
CustomProjectIconAsset, CustomProjectIconContent, DefaultAgentTerminalInterface, IdeaLanguage,
|
||||
IdeaUserSettings, ProjectIconRef, ProjectIconSet, ProjectSettings, ThemeColor,
|
||||
MAX_PROJECT_ICON_NAME_LEN,
|
||||
};
|
||||
|
||||
pub use orchestrator::{
|
||||
@ -283,19 +284,20 @@ pub use ports::{
|
||||
AssistantContextProvider, BackgroundCompletionStream, BackgroundTaskCompletion,
|
||||
BackgroundTaskHandle, BackgroundTaskPortError, BackgroundTaskRunner, BackgroundTaskSpec,
|
||||
BackgroundTaskStore, CliVersionReader, Clock, CompatibilityMatrixSource, ContextInjectionPlan,
|
||||
DirEntry, Embedder, EmbedderEnvInspector, EmbedderEnvReport, EmbedderError,
|
||||
EmbedderProfileStore, EmbedderPromptDismissal, EmbedderPromptStore, EventBus, EventStream,
|
||||
ExitStatus, FileSystem, FsError, GitCommitInfo, GitError, GitFileStatus, GitPort, GraphCommit,
|
||||
IdGenerator, IssueAttachmentContent, IssueNumberAllocator, IssueStore, IssueStoreError,
|
||||
LiveStateStore, LocalPath, McpToolPermissionStore, MemoryError, MemoryQuery, MemoryRecall,
|
||||
MemoryStore, ModelArtifactCancel, ModelArtifactDownloader, ModelArtifactProgress,
|
||||
ModelArtifactResolution, Output, OutputStream, PermissionStore, PluginManifestBytes,
|
||||
PluginManifestError, PluginManifestValidator, PluginMcpError, PluginMcpSupervisor,
|
||||
PluginPackageStore, PluginRegistryError, PluginRegistryStore, PluginStorageError,
|
||||
PluginStorageStore, PluginStoreError, PluginWorkspaceExecAuditSink, PreparedContext,
|
||||
ProcessError, ProcessSpawner, ProfileStore, ProjectPluginSettingsStore, ProjectSettingsStore,
|
||||
ProjectStore, ProviderModelCatalogue, PtyError, PtyHandle, PtyPort, RemoteError, RemoteHost,
|
||||
RemotePath, RuntimeError, RuntimePermissionProbe, ScheduledProjectTaskStore, ScheduledTask,
|
||||
CustomProjectIconAssetStore, CustomProjectIconStoreError, DirEntry, Embedder,
|
||||
EmbedderEnvInspector, EmbedderEnvReport, EmbedderError, EmbedderProfileStore,
|
||||
EmbedderPromptDismissal, EmbedderPromptStore, EventBus, EventStream, ExitStatus, FileSystem,
|
||||
FsError, GitCommitInfo, GitError, GitFileStatus, GitPort, GraphCommit, IdGenerator,
|
||||
IssueAttachmentContent, IssueNumberAllocator, IssueStore, IssueStoreError, LiveStateStore,
|
||||
LocalPath, McpToolPermissionStore, MemoryError, MemoryQuery, MemoryRecall, MemoryStore,
|
||||
ModelArtifactCancel, ModelArtifactDownloader, ModelArtifactProgress, ModelArtifactResolution,
|
||||
Output, OutputStream, PermissionStore, PluginManifestBytes, PluginManifestError,
|
||||
PluginManifestValidator, PluginMcpError, PluginMcpSupervisor, PluginPackageStore,
|
||||
PluginRegistryError, PluginRegistryStore, PluginStorageError, PluginStorageStore,
|
||||
PluginStoreError, PluginWorkspaceExecAuditSink, PreparedContext, ProcessError, ProcessSpawner,
|
||||
ProfileStore, ProjectPluginSettingsStore, ProjectSettingsStore, ProjectStore,
|
||||
ProviderModelCatalogue, PtyError, PtyHandle, PtyPort, RemoteError, RemoteHost, RemotePath,
|
||||
RuntimeError, RuntimePermissionProbe, ScheduledProjectTaskStore, ScheduledTask,
|
||||
ScheduledTaskStoreError, Scheduler, SpawnSpec, SprintStore, SprintStoreError, StoreError,
|
||||
StructuredSessionEnvironment, StructuredSessionEnvironmentPreparer, SystemPermissionStore,
|
||||
TemplateStore, UserSettingsStore, WindowStateStore, WorkspaceToolExecError,
|
||||
|
||||
@ -66,6 +66,7 @@ use crate::plugin::{
|
||||
use crate::profile::{AgentProfile, EmbedderProfile, StructuredAdapter};
|
||||
use crate::project::{Project, ProjectPath};
|
||||
use crate::remote::RemoteKind;
|
||||
use crate::settings::{CustomProjectIconAsset, CustomProjectIconContent};
|
||||
use crate::skill::{Skill, SkillScope};
|
||||
use crate::sprint::{Sprint, SprintIndexEntry, SprintVersion};
|
||||
use crate::system_permissions::{ProjectSystemPermissions, RuntimePermissionSnapshot};
|
||||
@ -564,6 +565,43 @@ pub trait ProjectSettingsStore: Send + Sync {
|
||||
) -> Result<(), StoreError>;
|
||||
}
|
||||
|
||||
/// Failures from the instance-local custom project icon library.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Error)]
|
||||
pub enum CustomProjectIconStoreError {
|
||||
/// Asset id is absent from the canonical index.
|
||||
#[error("custom project icon asset not found")]
|
||||
NotFound,
|
||||
/// Persisted data is malformed.
|
||||
#[error("custom project icon asset store format error: {0}")]
|
||||
Format(String),
|
||||
/// Filesystem operation failed.
|
||||
#[error("custom project icon asset store I/O error: {0}")]
|
||||
Io(String),
|
||||
}
|
||||
|
||||
/// Persistence for the workspace-wide user icon library.
|
||||
#[async_trait]
|
||||
pub trait CustomProjectIconAssetStore: Send + Sync {
|
||||
/// Lists persisted metadata in stable creation order.
|
||||
async fn list(&self) -> Result<Vec<CustomProjectIconAsset>, CustomProjectIconStoreError>;
|
||||
/// Reads metadata and bytes for one id.
|
||||
async fn read(
|
||||
&self,
|
||||
asset_id: &str,
|
||||
) -> Result<CustomProjectIconContent, CustomProjectIconStoreError>;
|
||||
/// Returns whether an id exists in the canonical index.
|
||||
async fn exists(&self, asset_id: &str) -> Result<bool, CustomProjectIconStoreError>;
|
||||
/// Atomically adds metadata and bytes under the validated extension.
|
||||
async fn create(
|
||||
&self,
|
||||
asset: &CustomProjectIconAsset,
|
||||
extension: &str,
|
||||
bytes: &[u8],
|
||||
) -> Result<(), CustomProjectIconStoreError>;
|
||||
/// Deletes one unused asset.
|
||||
async fn delete(&self, asset_id: &str) -> Result<(), CustomProjectIconStoreError>;
|
||||
}
|
||||
|
||||
/// Manifest parser and validator.
|
||||
pub trait PluginManifestValidator: Send + Sync {
|
||||
/// Validates raw manifest bytes.
|
||||
|
||||
@ -38,6 +38,8 @@ pub struct IdeaUserSettings {
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum ProjectIconSet {
|
||||
/// User-imported icon from the instance-local shared library.
|
||||
Custom,
|
||||
/// Unicode emoji catalogue.
|
||||
Emoji,
|
||||
/// Devicon language and platform catalogue.
|
||||
@ -50,6 +52,37 @@ pub enum ProjectIconSet {
|
||||
Idea,
|
||||
}
|
||||
|
||||
/// Metadata for one user-imported project icon.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct CustomProjectIconAsset {
|
||||
/// Opaque stable ASCII id used by [`ProjectIconRef`].
|
||||
pub id: String,
|
||||
/// Validated image media type.
|
||||
pub media_type: String,
|
||||
/// Stored byte count.
|
||||
pub byte_size: u64,
|
||||
/// Decoded pixel width.
|
||||
pub width: u32,
|
||||
/// Decoded pixel height.
|
||||
pub height: u32,
|
||||
/// Lowercase SHA-256 digest of the stored bytes.
|
||||
pub sha256: String,
|
||||
/// Basename supplied by the user; never a local path.
|
||||
pub original_filename: String,
|
||||
/// Creation time as epoch milliseconds.
|
||||
pub created_at: i64,
|
||||
}
|
||||
|
||||
/// Stored custom icon together with its validated image bytes.
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct CustomProjectIconContent {
|
||||
/// Persisted metadata.
|
||||
pub asset: CustomProjectIconAsset,
|
||||
/// Exact raster bytes.
|
||||
pub bytes: Vec<u8>,
|
||||
}
|
||||
|
||||
/// Maximum byte length of a stable project icon name.
|
||||
pub const MAX_PROJECT_ICON_NAME_LEN: usize = 64;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user