settings: replace timezone text field with searchable picker from chrono_tz::TZ_VARIANTS

This commit is contained in:
Greg Shuflin
2026-05-11 01:32:18 -07:00
parent 0ff4c325e6
commit cce568655b
6910 changed files with 36158 additions and 44 deletions
+1
View File
@@ -14,6 +14,7 @@ result-*
# Rust
rust/target/
rust/gui-app/target/
rust/**/*.rs.bk
# Android
@@ -125,6 +125,12 @@ class NativeLib {
fun setNodeName(name: String?) = uniffi.synchronicity.setNodeName(name)
fun getTimezone(): String? = uniffi.synchronicity.getTimezone()
fun setTimezone(tz: String?) = uniffi.synchronicity.setTimezone(tz)
fun listTimezones(): List<String> = uniffi.synchronicity.listTimezones()
fun getThemeMode(): String =
when (uniffi.synchronicity.getThemeMode()) {
ThemeMode.LIGHT -> "Light"
@@ -79,7 +79,7 @@ fun DocumentDetailScreen(documentId: String, onBack: () -> Unit) {
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
item { MetadataCard(document = document!!, totalBytes = totalBytes) }
item { MetadataCard(document = document!!, totalBytes = totalBytes, timezone = nativeLib.getTimezone()) }
item { PropertiesCard(properties = properties) }
item { ArtifactsCard(artifacts = artifacts) }
item { BlobRefsCard(blobRefs = blobRefs) }
@@ -91,7 +91,7 @@ fun DocumentDetailScreen(documentId: String, onBack: () -> Unit) {
}
@Composable
private fun MetadataCard(document: DocumentItem, totalBytes: Long) {
private fun MetadataCard(document: DocumentItem, totalBytes: Long, timezone: String? = null) {
Card(modifier = Modifier.fillMaxWidth()) {
Column(
modifier = Modifier.padding(16.dp),
@@ -100,7 +100,7 @@ private fun MetadataCard(document: DocumentItem, totalBytes: Long) {
Text(text = "Document Info", style = MaterialTheme.typography.titleMedium)
HorizontalDivider()
MetadataRow(label = "Type", value = formatDocumentType(document.docType))
MetadataRow(label = "Created", value = formatDate(document.createdAt))
MetadataRow(label = "Created", value = formatDate(document.createdAt, timezone))
MetadataRow(label = "Size", value = formatBytes(totalBytes))
// ID is shown in a SelectionContainer so the user can copy it.
Row(
@@ -15,12 +15,12 @@ data class DocumentDisplayModel(
val docType: String,
)
fun DocumentItem.toDisplayModel(): DocumentDisplayModel {
fun DocumentItem.toDisplayModel(timezone: String? = null): DocumentDisplayModel {
return DocumentDisplayModel(
id = id,
name = title.ifEmpty { "Untitled Document" },
type = formatDocumentType(docType),
date = formatDate(createdAt),
date = formatDate(createdAt, timezone),
size = sizeHintForDocType(docType),
docType = docType,
)
@@ -47,10 +47,13 @@ fun searchAndParseDocuments(
}
}
fun formatDate(isoDateString: String): String {
fun formatDate(isoDateString: String, timezone: String? = null): String {
return try {
val instant = java.time.Instant.parse(isoDateString)
val localDate = instant.atZone(java.time.ZoneId.systemDefault()).toLocalDate()
val zone = timezone?.let {
try { java.time.ZoneId.of(it) } catch (_: Exception) { null }
} ?: java.time.ZoneOffset.UTC
val localDate = instant.atZone(zone).toLocalDate()
val formatter = java.time.format.DateTimeFormatter.ofPattern("MMM dd, yyyy")
localDate.format(formatter)
} catch (e: Exception) {
@@ -94,7 +94,7 @@ fun DocumentsScreen(onBack: () -> Unit, onDocumentClick: ((String) -> Unit)? = n
types = selectedDocumentTypes.toTypedArray(),
dateFilter = selectedDateFilter ?: "",
)
.map { it.toDisplayModel() }
.map { it.toDisplayModel(nativeLib.getTimezone()) }
}
}
@@ -458,12 +458,15 @@ fun EnsembleScreen(nativeLib: NativeLib, onBack: () -> Unit) {
member.nodeIdHex
)
}
val tz = nativeLib.getTimezone()?.let {
runCatching { java.time.ZoneId.of(it) }.getOrNull()
} ?: java.time.ZoneOffset.UTC
val ts =
java.time.LocalTime.now()
java.time.ZonedDateTime.now(tz)
.format(
java.time.format
.DateTimeFormatter
.ofPattern("HH:mm:ss")
.ofPattern("HH:mm:ss z")
)
syncStates[member.nodeIdHex] =
SyncState(
@@ -37,7 +37,7 @@ fun PdfLibraryScreen(
// Load PDF documents from store
LaunchedEffect(refreshTrigger) {
pdfDocuments =
searchAndParseDocuments(nativeLib, types = arrayOf("pdf")).map { it.toDisplayModel() }
searchAndParseDocuments(nativeLib, types = arrayOf("pdf")).map { it.toDisplayModel(nativeLib.getTimezone()) }
}
SubAppScreen(title = "PDF Library", onBack = onBack) {
@@ -25,7 +25,7 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable
fun EpisodePlayerScreen(episode: PodcastEpisode, onBack: () -> Unit) {
fun EpisodePlayerScreen(episode: PodcastEpisode, onBack: () -> Unit, timezone: String? = null) {
val context = LocalContext.current
val audioPlayerManager = remember { AudioPlayerProvider.getAudioPlayerManager(context) }
val playbackState by audioPlayerManager.playbackState.collectAsState()
@@ -75,7 +75,7 @@ fun EpisodePlayerScreen(episode: PodcastEpisode, onBack: () -> Unit) {
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
Text(
text = formatDate(episode.publishDate),
text = formatDate(episode.publishDate, timezone),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
@@ -94,7 +94,7 @@ fun PodcastCoverArt(podcast: Podcast, modifier: Modifier = Modifier) {
}
@Composable
fun PodcastItem(podcast: Podcast, onClick: () -> Unit) {
fun PodcastItem(podcast: Podcast, onClick: () -> Unit, timezone: String? = null) {
Card(modifier = Modifier.fillMaxWidth().clickable(onClick = onClick)) {
Row(
modifier = Modifier.fillMaxWidth().padding(16.dp),
@@ -123,7 +123,7 @@ fun PodcastItem(podcast: Podcast, onClick: () -> Unit) {
overflow = TextOverflow.Ellipsis,
)
Text(
text = "Last updated: ${formatDate(podcast.lastUpdated)}",
text = "Last updated: ${formatDate(podcast.lastUpdated, timezone)}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
)
@@ -133,7 +133,7 @@ fun PodcastItem(podcast: Podcast, onClick: () -> Unit) {
}
@Composable
fun EpisodeItem(episode: PodcastEpisode, onClick: () -> Unit) {
fun EpisodeItem(episode: PodcastEpisode, onClick: () -> Unit, timezone: String? = null) {
Card(modifier = Modifier.fillMaxWidth().clickable(onClick = onClick)) {
Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
Text(
@@ -157,7 +157,7 @@ fun EpisodeItem(episode: PodcastEpisode, onClick: () -> Unit) {
style = MaterialTheme.typography.bodySmall,
)
Text(
text = formatDate(episode.publishDate),
text = formatDate(episode.publishDate, timezone),
style = MaterialTheme.typography.bodySmall,
)
}
@@ -18,30 +18,31 @@ fun formatDuration(durationMs: Long): String {
}
}
fun formatDate(timestamp: Instant): String {
val formatter =
java.time.format.DateTimeFormatter.ofPattern("MMM d, yyyy")
.withZone(java.time.ZoneId.systemDefault())
fun formatDate(timestamp: Instant, timezone: String? = null): String {
val zone = timezone?.let {
try { java.time.ZoneId.of(it) } catch (_: Exception) { null }
} ?: java.time.ZoneOffset.UTC
val formatter = java.time.format.DateTimeFormatter.ofPattern("MMM d, yyyy").withZone(zone)
return formatter.format(timestamp)
}
fun Podcast.toDocumentDisplayModel(): DocumentDisplayModel {
fun Podcast.toDocumentDisplayModel(timezone: String? = null): DocumentDisplayModel {
return DocumentDisplayModel(
id = id,
name = title,
type = formatDocumentType("podcast_feed"),
date = formatDate(lastUpdated),
date = formatDate(lastUpdated, timezone),
size = "Podcast",
docType = "podcast_feed",
)
}
fun PodcastEpisode.toDocumentDisplayModel(): DocumentDisplayModel {
fun PodcastEpisode.toDocumentDisplayModel(timezone: String? = null): DocumentDisplayModel {
return DocumentDisplayModel(
id = id,
name = title,
type = formatDocumentType("podcast_episode"),
date = formatDate(publishDate),
date = formatDate(publishDate, timezone),
size = formatDuration(duration),
docType = "podcast_episode",
)
@@ -1,7 +1,11 @@
package com.gregshuflin.synchronicity.settings
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.*
@@ -30,6 +34,13 @@ fun SettingsView(themeState: ThemeState, onBack: () -> Unit, onNodeKeyClick: ()
// Node name state — loaded once, edited locally, saved on confirm.
var nodeName by remember { mutableStateOf(nativeLib.getNodeName() ?: "") }
var nodeNameSaved by remember { mutableStateOf(false) }
val allTimezones = remember { nativeLib.listTimezones() }
var timezoneSearch by remember { mutableStateOf("") }
var timezoneSaved by remember { mutableStateOf(nativeLib.getTimezone() ?: "") }
val filteredTimezones = remember(timezoneSearch) {
if (timezoneSearch.isBlank()) emptyList()
else allTimezones.filter { it.contains(timezoneSearch, ignoreCase = true) }
}
SubAppScreen(title = "Settings", onBack = onBack) {
// Settings content
@@ -107,6 +118,75 @@ fun SettingsView(themeState: ThemeState, onBack: () -> Unit, onNodeKeyClick: ()
}
}
// Timezone picker
Text(
text = "Timezone",
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onBackground,
modifier = Modifier.padding(top = 8.dp, bottom = 4.dp),
)
Text(
text = "Select an IANA timezone for displaying times. Leave unset to use UTC.",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.6f),
modifier = Modifier.padding(bottom = 4.dp),
)
if (timezoneSaved.isNotBlank()) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "Current: $timezoneSaved",
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier.weight(1f),
)
TextButton(onClick = {
nativeLib.setTimezone(null)
timezoneSaved = ""
timezoneSearch = ""
}) {
Text("Clear")
}
}
}
OutlinedTextField(
value = timezoneSearch,
onValueChange = { timezoneSearch = it },
modifier = Modifier.fillMaxWidth(),
placeholder = { Text("Search timezones…") },
label = { Text("Search") },
singleLine = true,
)
if (filteredTimezones.isNotEmpty()) {
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.heightIn(max = 300.dp)
.border(
1.dp,
MaterialTheme.colorScheme.outlineVariant,
RoundedCornerShape(4.dp),
),
) {
items(filteredTimezones) { tz ->
Text(
text = tz,
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.fillMaxWidth()
.clickable {
nativeLib.setTimezone(tz)
timezoneSaved = tz
timezoneSearch = ""
}
.padding(horizontal = 16.dp, vertical = 12.dp),
)
HorizontalDivider()
}
}
}
// Spacer to push dangerous actions to bottom
Spacer(modifier = Modifier.weight(1f))
@@ -673,6 +673,8 @@ internal object IntegrityCheckingUniffiLib {
): Short
external fun uniffi_synchronicity_checksum_func_get_theme_mode(
): Short
external fun uniffi_synchronicity_checksum_func_get_timezone(
): Short
external fun uniffi_synchronicity_checksum_func_import_image_document(
): Short
external fun uniffi_synchronicity_checksum_func_import_music_track_document(
@@ -731,6 +733,8 @@ internal object IntegrityCheckingUniffiLib {
): Short
external fun uniffi_synchronicity_checksum_func_set_theme_mode(
): Short
external fun uniffi_synchronicity_checksum_func_set_timezone(
): Short
external fun uniffi_synchronicity_checksum_func_start_p2p_node(
): Short
external fun uniffi_synchronicity_checksum_func_stop_p2p_node(
@@ -794,6 +798,8 @@ external fun uniffi_synchronicity_fn_func_get_seen_nodes(uniffi_out_err: UniffiR
): RustBuffer.ByValue
external fun uniffi_synchronicity_fn_func_get_theme_mode(uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun uniffi_synchronicity_fn_func_get_timezone(uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun uniffi_synchronicity_fn_func_import_image_document(`fileName`: RustBuffer.ByValue,`fileExt`: RustBuffer.ByValue,`imageBytes`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
): RustBuffer.ByValue
external fun uniffi_synchronicity_fn_func_import_music_track_document(`fileName`: RustBuffer.ByValue,`musicBytes`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
@@ -852,6 +858,8 @@ external fun uniffi_synchronicity_fn_func_set_node_name(`name`: RustBuffer.ByVal
): Unit
external fun uniffi_synchronicity_fn_func_set_theme_mode(`themeMode`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
): Unit
external fun uniffi_synchronicity_fn_func_set_timezone(`tz`: RustBuffer.ByValue,uniffi_out_err: UniffiRustCallStatus,
): Unit
external fun uniffi_synchronicity_fn_func_start_p2p_node(uniffi_out_err: UniffiRustCallStatus,
): Unit
external fun uniffi_synchronicity_fn_func_stop_p2p_node(uniffi_out_err: UniffiRustCallStatus,
@@ -1040,6 +1048,9 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) {
if (lib.uniffi_synchronicity_checksum_func_get_theme_mode() != 45849.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_synchronicity_checksum_func_get_timezone() != 14416.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_synchronicity_checksum_func_import_image_document() != 55567.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
@@ -1127,6 +1138,9 @@ private fun uniffiCheckApiChecksums(lib: IntegrityCheckingUniffiLib) {
if (lib.uniffi_synchronicity_checksum_func_set_theme_mode() != 47590.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_synchronicity_checksum_func_set_timezone() != 29610.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
if (lib.uniffi_synchronicity_checksum_func_start_p2p_node() != 8361.toShort()) {
throw RuntimeException("UniFFI API checksum mismatch: try cleaning and rebuilding your project")
}
@@ -3023,6 +3037,17 @@ public typealias FfiConverterTypeTimestamp = FfiConverterString
}
@Throws(SyncException::class) fun `getTimezone`(): kotlin.String? {
return FfiConverterOptionalString.lift(
uniffiRustCallWithError(SyncException) { _status ->
UniffiLib.uniffi_synchronicity_fn_func_get_timezone(
_status)
}
)
}
@Throws(SyncException::class) fun `importImageDocument`(`fileName`: kotlin.String, `fileExt`: kotlin.String, `imageBytes`: kotlin.ByteArray): kotlin.String {
return FfiConverterString.lift(
uniffiRustCallWithError(SyncException) { _status ->
@@ -3331,6 +3356,16 @@ public typealias FfiConverterTypeTimestamp = FfiConverterString
@Throws(SyncException::class) fun `setTimezone`(`tz`: kotlin.String?)
=
uniffiRustCallWithError(SyncException) { _status ->
UniffiLib.uniffi_synchronicity_fn_func_set_timezone(
FfiConverterOptionalString.lower(`tz`),_status)
}
@Throws(SyncException::class) fun `startP2pNode`()
=
uniffiRustCallWithError(SyncException) { _status ->
+12181
View File
File diff suppressed because it is too large Load Diff
+1
View File
@@ -21,6 +21,7 @@ tokio = { version = "1.49", features = ["full"] }
tokio-stream = "0.1"
arboard = "3.6"
chrono = { version = "0.4", features = ["serde"] }
chrono-tz = "0.10"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
image = "0.25"
+27 -13
View File
@@ -14,8 +14,10 @@ use synchronicity::stores::artifact::ArtifactStore;
use synchronicity::stores::blob_store::BlobStore;
use synchronicity::stores::document::DocumentStore;
use synchronicity::stores::node_document_status::{NodeDocumentPresence, NodeDocumentStore};
use synchronicity::stores::settings::SettingsStore;
use synchronicity::vault::DEFAULT_VAULT_ID;
use crate::util;
use crate::{AppWindow, DocumentDisplay, DocumentItemDisplay, DocumentNodeStatusDisplay};
pub fn setup_document_callbacks(
@@ -29,6 +31,7 @@ pub fn setup_document_callbacks(
{
let document_store = DocumentStore::new(db_path_str)?;
let artifact_store = ArtifactStore::new(db_path_str)?;
let settings_store = SettingsStore::new(db_path_str)?;
let ui_weak = ui.as_weak();
ui.on_load_documents(move || {
info!("Load documents requested");
@@ -36,7 +39,8 @@ pub fn setup_document_callbacks(
ui.set_documents_loading(true);
ui.set_documents_error("".into());
match load_documents_with_titles(&document_store, &artifact_store) {
let tz = util::load_tz(&settings_store);
match load_documents_with_titles(&document_store, &artifact_store, tz) {
Ok(model) => {
info!("Loaded {} documents from database", model.row_count());
ui.set_documents(std::rc::Rc::new(model).into());
@@ -57,6 +61,7 @@ pub fn setup_document_callbacks(
{
let document_store = DocumentStore::new(db_path_str)?;
let artifact_store = ArtifactStore::new(db_path_str)?;
let settings_store_detail = SettingsStore::new(db_path_str)?;
let local_node_for_detail = Arc::clone(&local_node);
let ui_weak = ui.as_weak();
ui.on_load_document_detail(move |document_id| {
@@ -70,9 +75,10 @@ pub fn setup_document_callbacks(
}
};
let tz_detail = util::load_tz(&settings_store_detail);
match document_store.list_properties(&doc_id) {
Ok(props) => {
let display = create_properties_display(&props);
let display = create_properties_display(&props, tz_detail);
ui.set_document_detail_properties(
std::rc::Rc::new(slint::VecModel::from(display)).into(),
);
@@ -82,7 +88,7 @@ pub fn setup_document_callbacks(
match artifact_store.list_artifacts(&doc_id) {
Ok(artifacts) => {
let display = create_artifacts_display(&artifacts);
let display = create_artifacts_display(&artifacts, tz_detail);
ui.set_document_detail_artifacts(
std::rc::Rc::new(slint::VecModel::from(display)).into(),
);
@@ -103,7 +109,7 @@ pub fn setup_document_callbacks(
let local_pubkey = local_node_for_detail.get().copied();
match node_doc_store.list_nodes_for_document(&doc_id) {
Ok(records) => {
let display = create_node_status_display(&records, local_pubkey);
let display = create_node_status_display(&records, local_pubkey, tz_detail);
ui.set_document_node_statuses(
std::rc::Rc::new(slint::VecModel::from(display)).into(),
);
@@ -278,6 +284,7 @@ fn import_pdf(
fn load_documents_with_titles(
document_store: &DocumentStore,
artifact_store: &ArtifactStore,
tz: Option<chrono_tz::Tz>,
) -> Result<slint::VecModel<DocumentDisplay>, Box<dyn std::error::Error>> {
let documents = document_store.list_documents()?;
let blob_sizes = document_store.blob_bytes_per_document()?;
@@ -289,7 +296,7 @@ fn load_documents_with_titles(
document_store.get_title(&doc.id)?.unwrap_or_else(|| "Untitled Document".to_string());
let document_type = format_document_type(&doc.doc_type);
let document_type_icon = icon_for_document_type(&doc.doc_type);
let created_at = doc.created_at.format("%b %d, %Y").to_string();
let created_at = util::format_date(*doc.created_at, tz);
let id_str = doc.id.to_string();
let total_bytes = blob_sizes.get(&id_str).copied().unwrap_or(0)
+ artifact_sizes.get(&id_str).copied().unwrap_or(0);
@@ -331,23 +338,29 @@ fn format_document_type(doc_type: &DocumentType) -> &'static str {
}
}
fn create_properties_display(props: &[Property]) -> Vec<DocumentItemDisplay> {
fn create_properties_display(
props: &[Property],
tz: Option<chrono_tz::Tz>,
) -> Vec<DocumentItemDisplay> {
props
.iter()
.map(|prop| DocumentItemDisplay {
item_type: prop.kind.type_name().into(),
modified_at: prop.modified_at.format("%b %d, %Y %H:%M").to_string().into(),
summary: summarize_property_value(&prop.value).into(),
modified_at: util::format_datetime(*prop.modified_at, tz).into(),
summary: summarize_property_value(&prop.value, tz).into(),
})
.collect()
}
fn create_artifacts_display(artifacts: &[Artifact]) -> Vec<DocumentItemDisplay> {
fn create_artifacts_display(
artifacts: &[Artifact],
tz: Option<chrono_tz::Tz>,
) -> Vec<DocumentItemDisplay> {
artifacts
.iter()
.map(|artifact| DocumentItemDisplay {
item_type: artifact.role.type_name().into(),
modified_at: artifact.modified_at.format("%b %d, %Y %H:%M").to_string().into(),
modified_at: util::format_datetime(*artifact.modified_at, tz).into(),
summary: summarize_artifact_value(&artifact.value).into(),
})
.collect()
@@ -403,6 +416,7 @@ fn summarize_artifact_value(value: &ArtifactValue) -> String {
fn create_node_status_display(
records: &[synchronicity::stores::node_document_status::NodeDocumentRecord],
local_pubkey: Option<NodePubkey>,
tz: Option<chrono_tz::Tz>,
) -> Vec<DocumentNodeStatusDisplay> {
records
.iter()
@@ -413,7 +427,7 @@ fn create_node_status_display(
NodeDocumentPresence::Present => "Present",
NodeDocumentPresence::Removed => "Removed",
};
let observed_at = rec.observed_at.0.format("%b %d, %Y %H:%M").to_string();
let observed_at = util::format_datetime(*rec.observed_at, tz);
let is_local = local_pubkey.map_or(false, |lp| lp == rec.node_pubkey);
DocumentNodeStatusDisplay {
node_id: node_id.into(),
@@ -425,7 +439,7 @@ fn create_node_status_display(
.collect()
}
fn summarize_property_value(value: &PropertyValue) -> String {
fn summarize_property_value(value: &PropertyValue, tz: Option<chrono_tz::Tz>) -> String {
match value {
PropertyValue::Text(s) => {
if s.len() > 120 {
@@ -435,7 +449,7 @@ fn summarize_property_value(value: &PropertyValue) -> String {
}
}
PropertyValue::Tags(tags) => tags.join(", "),
PropertyValue::Timestamp(ts) => ts.format("%Y-%m-%d %H:%M UTC").to_string(),
PropertyValue::Timestamp(ts) => util::format_datetime(ts.0, tz),
PropertyValue::F64(v) => format!("{v:.2}"),
PropertyValue::U32(v) => v.to_string(),
}
+3 -1
View File
@@ -132,6 +132,8 @@ pub fn setup_ensemble_callbacks(
let local_node = SettingsStore::new(&db_path)
.ok()
.and_then(|store| store.local_node_pubkey().ok().flatten());
let tz_for_sync =
SettingsStore::new(&db_path).ok().and_then(|store| crate::util::load_tz(&store));
std::thread::spawn(move || {
let (ok, err, timestamp) = match (node_id_hex, endpoint, local_node) {
@@ -161,7 +163,7 @@ pub fn setup_ensemble_callbacks(
});
match result {
Ok(n) => {
let ts = chrono::Local::now().format("%H:%M:%S").to_string();
let ts = crate::util::format_now_time(tz_for_sync);
(format!("synced {n} doc(s)"), String::new(), ts)
}
Err(e) => (String::new(), e.to_string(), String::new()),
+80
View File
@@ -418,6 +418,86 @@ fn slint_main(store: Store, cache_dir: PathBuf) -> Result<(), Box<dyn std::error
});
}
// Initialize timezone-current from stored settings
{
let tz_name = SettingsStore::new(db_path_str)?
.load_settings()
.ok()
.flatten()
.and_then(|s| s.timezone)
.map(|tz| tz.name().to_string())
.unwrap_or_default();
ui.set_timezone_current(tz_name.into());
}
// Timezone search: filter chrono_tz::TZ_VARIANTS by query and update suggestions
{
let ui_weak = ui.as_weak();
ui.on_timezone_search_changed(move |query| {
let q = query.trim().to_lowercase();
let suggestions: Vec<slint::SharedString> = if q.is_empty() {
vec![]
} else {
chrono_tz::TZ_VARIANTS
.iter()
.filter(|tz| tz.name().to_lowercase().contains(&q))
.take(100)
.map(|tz| tz.name().into())
.collect()
};
if let Some(ui) = ui_weak.upgrade() {
ui.set_timezone_suggestions(
std::rc::Rc::new(slint::VecModel::from(suggestions)).into(),
);
}
});
}
// Timezone select: save the chosen timezone, clear the search
{
let settings_store_clone = SettingsStore::new(db_path_str)?;
let ui_weak = ui.as_weak();
ui.on_select_timezone(move |tz_name| {
use std::str::FromStr;
match chrono_tz::Tz::from_str(&tz_name) {
Ok(tz) => {
if let Err(e) = settings_store_clone.set_timezone(Some(tz)) {
log::error!("Failed to save timezone: {e}");
return;
}
if let Some(ui) = ui_weak.upgrade() {
ui.set_timezone_current(tz_name);
ui.set_timezone_search("".into());
ui.set_timezone_suggestions(
std::rc::Rc::new(slint::VecModel::<slint::SharedString>::default())
.into(),
);
}
}
Err(_) => log::error!("select_timezone called with invalid tz: {tz_name}"),
}
});
}
// Timezone clear: remove timezone, reset search
{
let settings_store_clone = SettingsStore::new(db_path_str)?;
let ui_weak = ui.as_weak();
ui.on_clear_timezone(move || {
if let Err(e) = settings_store_clone.set_timezone(None) {
log::error!("Failed to clear timezone: {e}");
return;
}
if let Some(ui) = ui_weak.upgrade() {
ui.set_timezone_current("".into());
ui.set_timezone_search("".into());
ui.set_timezone_suggestions(
std::rc::Rc::new(slint::VecModel::<slint::SharedString>::default()).into(),
);
}
});
}
step("ui callbacks wired (pre-domain)");
// Setup notes callbacks, then eagerly load data now that callbacks are registered.
+10 -3
View File
@@ -4,7 +4,9 @@ use slint::ComponentHandle;
use synchronicity::documents::{DocumentID, DocumentType, Role};
use synchronicity::stores::blob_store::BlobStore;
use synchronicity::stores::document::DocumentStore;
use synchronicity::stores::settings::SettingsStore;
use crate::util;
use crate::{AppWindow, TreeItem};
type PageBuffer = (slint::SharedPixelBuffer<slint::Rgb8Pixel>, i32);
@@ -119,16 +121,18 @@ pub fn setup_pdf_viewer_callbacks(
{
let db_path = db_path_str.to_string();
let settings_store_pdf = SettingsStore::new(db_path_str)?;
let ui_weak = ui.as_weak();
ui.on_load_pdf_documents(move || {
info!("Load PDF documents requested");
let ui_weak = ui_weak.clone();
let db_path = db_path.clone();
let tz = util::load_tz(&settings_store_pdf);
std::thread::spawn(move || {
let result = DocumentStore::new(&db_path)
.map_err(|e| format!("{e}"))
.and_then(load_pdf_document_list);
.and_then(|store| load_pdf_document_list(store, tz));
let _ = ui_weak.upgrade_in_event_loop(move |ui| match result {
Ok(items) => {
@@ -143,7 +147,10 @@ pub fn setup_pdf_viewer_callbacks(
Ok(())
}
fn load_pdf_document_list(store: DocumentStore) -> Result<Vec<TreeItem>, String> {
fn load_pdf_document_list(
store: DocumentStore,
tz: Option<chrono_tz::Tz>,
) -> Result<Vec<TreeItem>, String> {
let docs = store.list_documents_by_type(&DocumentType::Pdf).map_err(|e| format!("{e}"))?;
let mut items = Vec::with_capacity(docs.len());
for doc in docs {
@@ -151,7 +158,7 @@ fn load_pdf_document_list(store: DocumentStore) -> Result<Vec<TreeItem>, String>
.get_title(&doc.id)
.map_err(|e| format!("{e}"))?
.unwrap_or_else(|| "Untitled".to_string());
let created_at = doc.created_at.format("%b %d, %Y").to_string();
let created_at = util::format_date(*doc.created_at, tz);
items.push(TreeItem {
id: doc.id.to_string().into(),
label: title.into(),
+8 -2
View File
@@ -6,8 +6,11 @@ use synchronicity::{
documents::{Document, DocumentType, Property, PropertyKind, PropertyValue, Timestamp},
node::NodePubkey,
stores::document::DocumentStore,
stores::settings::SettingsStore,
};
use crate::util;
use crate::image_cache::{ImageCache, RawImageData};
// Include the generated Slint code - we need the types
@@ -177,12 +180,14 @@ pub fn setup_podcast_callbacks(
// Connect callback for loading podcast episodes
{
let ui_weak = ui.as_weak();
let settings_store_episodes = SettingsStore::new(db_path_str)?;
ui.on_load_podcast_episodes(move |podcast_id| {
info!("Load podcast episodes requested for podcast ID: {podcast_id}");
if let Some(ui) = ui_weak.upgrade() {
set_episodes_loading_state(&ui, true, "");
let ui_weak_for_thread = ui_weak.clone();
let podcast_id_string = podcast_id.to_string();
let tz_for_episodes = util::load_tz(&settings_store_episodes);
std::thread::spawn(move || {
match podcast_id_string.parse::<i64>() {
Ok(id) => {
@@ -190,7 +195,7 @@ pub fn setup_podcast_callbacks(
Ok(json_response) => {
match serde_json::from_str::<synchronicity::agents::podcast::PodcastEpisodesResponse>(&json_response) {
Ok(response) => {
let episodes = convert_episodes(response);
let episodes = convert_episodes(response, tz_for_episodes);
info!("Loaded {} episodes for podcast {}", episodes.len(), podcast_id_string);
invoke_on_ui_thread(ui_weak_for_thread.clone(), move |ui| {
let episodes_model = slint::VecModel::from(episodes);
@@ -447,6 +452,7 @@ fn parse_timestamp(s: &str) -> Result<Timestamp, ()> {
fn convert_episodes(
response: synchronicity::agents::podcast::PodcastEpisodesResponse,
tz: Option<chrono_tz::Tz>,
) -> Vec<PodcastEpisode> {
response
.items
@@ -455,7 +461,7 @@ fn convert_episodes(
.map(|episode| {
let date_published = episode.date_published_pretty.unwrap_or_else(|| {
chrono::DateTime::from_timestamp(episode.date_published, 0)
.map(|dt| dt.format("%B %d, %Y").to_string())
.map(|dt| util::format_date(dt, tz))
.unwrap_or_else(|| "Unknown date".to_string())
});
+30
View File
@@ -10,6 +10,36 @@ pub fn get_default_cache_path() -> PathBuf {
}
}
/// Format a UTC datetime as a date string, converting to the given timezone if provided.
pub fn format_date(ts: chrono::DateTime<chrono::Utc>, tz: Option<chrono_tz::Tz>) -> String {
match tz {
None => ts.format("%b %d, %Y").to_string(),
Some(tz) => ts.with_timezone(&tz).format("%b %d, %Y").to_string(),
}
}
/// Format a UTC datetime as a date+time string, converting to the given timezone if provided.
pub fn format_datetime(ts: chrono::DateTime<chrono::Utc>, tz: Option<chrono_tz::Tz>) -> String {
match tz {
None => ts.format("%b %d, %Y %H:%M UTC").to_string(),
Some(tz) => ts.with_timezone(&tz).format("%b %d, %Y %H:%M %Z").to_string(),
}
}
/// Format the current time as HH:MM:SS, converting to the given timezone if provided.
pub fn format_now_time(tz: Option<chrono_tz::Tz>) -> String {
let now = chrono::Utc::now();
match tz {
None => now.format("%H:%M:%S UTC").to_string(),
Some(tz) => now.with_timezone(&tz).format("%H:%M:%S %Z").to_string(),
}
}
/// Load the configured timezone from a settings store (returns None if unset or on error).
pub fn load_tz(store: &synchronicity::stores::settings::SettingsStore) -> Option<chrono_tz::Tz> {
store.load_settings().ok().flatten().and_then(|s| s.timezone)
}
/// Simple clipboard implementation using arboard
pub fn copy_to_clipboard(text: &str) -> Result<(), Box<dyn std::error::Error>> {
use arboard::Clipboard;
+1
View File
@@ -0,0 +1 @@
{"rustc_fingerprint":9337824829045165864,"outputs":{"7971740275564407648":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/greg/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.95.0 (59807616e 2026-04-14)\nbinary: rustc\ncommit-hash: 59807616e1fa2540724bfbac14d7976d7e4a3860\ncommit-date: 2026-04-14\nhost: x86_64-unknown-linux-gnu\nrelease: 1.95.0\nLLVM version: 22.1.2\n","stderr":""}},"successes":{}}
+3
View File
@@ -0,0 +1,3 @@
Signature: 8a477f597d28d172789f06886806bc55
# This file is a cache directory tag created by cargo.
# For information about cache directory tags see https://bford.info/cachedir/
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
2973cb03070a41a9
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"default\", \"gvar-alloc\", \"std\", \"variable-fonts\"]","declared_features":"[\"default\", \"gvar-alloc\", \"libm\", \"std\", \"variable-fonts\"]","target":11794240345726188307,"profile":15657897354478470176,"path":453923608936822346,"deps":[[4945662571602681759,"ab_glyph_rasterizer",false,1835265233106688581],[5327495677235252177,"owned_ttf_parser",false,8384167163761158203]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/ab_glyph-26d10c9d7fb33b85/dep-lib-ab_glyph","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"default\", \"std\"]","declared_features":"[\"default\", \"libm\", \"std\"]","target":4335109392423587462,"profile":15657897354478470176,"path":10994903077493635844,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/ab_glyph_rasterizer-e4817a80f37d7712/dep-lib-ab_glyph_rasterizer","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
fe054957df236c28
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[]","declared_features":"[\"enumn\", \"pyo3\", \"schemars\", \"serde\"]","target":664360014476758310,"profile":15657897354478470176,"path":12361977572600874603,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/accesskit-7626a3b8a335de15/dep-lib-accesskit","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[]","declared_features":"[\"simplified-api\"]","target":17912706900245267029,"profile":15657897354478470176,"path":7201284552552190884,"deps":[[689005112783428672,"accesskit",false,2912742501150950910],[3069055572107966020,"atspi_common",false,4078112881298510816],[4639055201516615152,"accesskit_consumer",false,2495518273799746603],[10261850983220459744,"zvariant",false,8675036769754946197],[13548984313718623784,"serde",false,14771478149853119014]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/accesskit_atspi_common-f1ff455d6f8adb97/dep-lib-accesskit_atspi_common","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[]","declared_features":"[]","target":3496681106653858094,"profile":15657897354478470176,"path":7041939288721677534,"deps":[[689005112783428672,"accesskit",false,2912742501150950910],[17037126617600641945,"hashbrown",false,9660354064269554462]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/accesskit_consumer-1bf90e516c1537d8/dep-lib-accesskit_consumer","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"async-io\"]","declared_features":"[\"async-io\", \"default\", \"tokio\"]","target":6277445120267301956,"profile":15657897354478470176,"path":14483877461928300721,"deps":[[587433499505765785,"zbus",false,16358011700513314653],[689005112783428672,"accesskit",false,2912742501150950910],[867502981669738401,"async_task",false,7334983561989410596],[5898568623609459682,"futures_util",false,15333998057161168100],[6633419628244209595,"async_channel",false,13366297935816536370],[9090520973410485560,"futures_lite",false,6061602431063719799],[13084009532035793779,"accesskit_atspi_common",false,11485607286164989340],[13548984313718623784,"serde",false,14771478149853119014],[16784658679919228589,"async_executor",false,15897794178730384989],[18419353160988999580,"atspi",false,3056199218607239995]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/accesskit_unix-1c956af1387a6d72/dep-lib-accesskit_unix","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"accesskit_unix\", \"async-io\", \"rwh_06\"]","declared_features":"[\"accesskit_android\", \"accesskit_unix\", \"async-io\", \"default\", \"rwh_05\", \"rwh_06\", \"tokio\"]","target":14667884907678119804,"profile":15657897354478470176,"path":2687282872012684910,"deps":[[689005112783428672,"accesskit",false,2912742501150950910],[4143744114649553716,"rwh_06",false,2645763398035123333],[6305327886559338045,"winit",false,5973309495064764745],[16298787009127776788,"accesskit_unix",false,10908122962744241488]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/accesskit_winit-94913cdefe354264/dep-lib-accesskit_winit","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
309ad8d0987a0f8d
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[]","declared_features":"[\"core\", \"default\", \"rustc-dep-of-std\", \"std\"]","target":6569825234462323107,"profile":15657897354478470176,"path":10084672383060434313,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/adler2-5305f511e1c31af3/dep-lib-adler2","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
892cd496bfe66141
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"alloc\", \"getrandom\", \"rand_core\"]","declared_features":"[\"alloc\", \"arrayvec\", \"blobby\", \"bytes\", \"default\", \"dev\", \"getrandom\", \"heapless\", \"rand_core\", \"std\", \"stream\"]","target":6415113071054268027,"profile":15657897354478470176,"path":12618130129449490657,"deps":[[6039282458970808711,"crypto_common",false,122976906069224045],[10520923840501062997,"generic_array",false,17112515513825179283]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aead-8b05e28881447ab1/dep-lib-aead","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
b1026910809c10f8
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[]","declared_features":"[\"hazmat\", \"zeroize\"]","target":1651443328692853038,"profile":15657897354478470176,"path":16866124705301701546,"deps":[[7667230146095136825,"cfg_if",false,4359982452671240704],[7916416211798676886,"cipher",false,5892701142336791316],[17620084158052398167,"cpufeatures",false,8699061790002953938]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aes-2fb46397177e2c02/dep-lib-aes","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
99e8cf245d9dedc2
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"aes\"]","declared_features":"[\"aes\", \"alloc\", \"arrayvec\", \"default\", \"getrandom\", \"heapless\", \"rand_core\", \"std\", \"stream\", \"zeroize\"]","target":6327482228044654328,"profile":15657897354478470176,"path":2402980718893287865,"deps":[[5822136307240319171,"ctr",false,12006938113363605998],[7916416211798676886,"cipher",false,5892701142336791316],[17003143334332120809,"subtle",false,12728970595675519547],[17625407307438784893,"aes",false,17874958994878562993],[17797166225172937111,"aead",false,4711300395749878921],[18030706926766528332,"ghash",false,13454567624705747670]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aes-gcm-e478a71ede9636d2/dep-lib-aes_gcm","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
a9027464ac0861c4
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"default\", \"getrandom\", \"no-rng\", \"runtime-rng\", \"std\"]","declared_features":"[\"atomic-polyfill\", \"compile-time-rng\", \"const-random\", \"default\", \"getrandom\", \"nightly-arm-aes\", \"no-rng\", \"runtime-rng\", \"serde\", \"std\"]","target":8470944000320059508,"profile":15657897354478470176,"path":12028439975685163916,"deps":[[966925859616469517,"build_script_build",false,636672640033982289],[3612005756660025491,"zerocopy",false,9044444129476688459],[5855319743879205494,"once_cell",false,2111388815816723308],[7667230146095136825,"cfg_if",false,4359982452671240704],[18408407127522236545,"getrandom",false,17664883919403800260]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/ahash-6ac051d3d5e45460/dep-lib-ahash","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"","declared_features":"","target":0,"profile":0,"path":0,"deps":[[966925859616469517,"build_script_build",false,13440271027672800907]],"local":[{"RerunIfChanged":{"output":"debug/build/ahash-a57948a68d40e0a7/output","paths":["build.rs"]}}],"rustflags":[],"config":0,"compile_kind":0}
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"default\", \"getrandom\", \"no-rng\", \"runtime-rng\", \"std\"]","declared_features":"[\"atomic-polyfill\", \"compile-time-rng\", \"const-random\", \"default\", \"getrandom\", \"nightly-arm-aes\", \"no-rng\", \"runtime-rng\", \"serde\", \"std\"]","target":17883862002600103897,"profile":2225463790103693989,"path":14294242299832124392,"deps":[[5398981501050481332,"version_check",false,3744412454811588828]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/ahash-d6dd36af1ebc73ff/dep-build-script-build-script-build","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
744a8fde0311ec8e
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"perf-literal\", \"std\"]","declared_features":"[\"default\", \"logging\", \"perf-literal\", \"std\"]","target":7534583537114156500,"profile":15657897354478470176,"path":10454787857890120135,"deps":[[1363051979936526615,"memchr",false,11543152680481457274]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aho-corasick-eca903c8c5dd1d29/dep-lib-aho_corasick","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
d7124fe92d902e14
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[]","declared_features":"[]","target":3125153431088264391,"profile":15657897354478470176,"path":4845643003795558085,"deps":[[3479621775654468824,"as_slice",false,4886650512974528489]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aligned-803e65ab7cfaf5b4/dep-lib-aligned","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
e795a3a21f3eac2f
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[]","declared_features":"[]","target":3125153431088264391,"profile":15657897354478470176,"path":4845643003795558085,"deps":[[3479621775654468824,"as_slice",false,6199182422229431844]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aligned-fc7fa5908837a244/dep-lib-aligned","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
77044b8768ea3015
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"default\", \"std\"]","declared_features":"[\"default\", \"serde\", \"std\"]","target":2676654459276378593,"profile":15657897354478470176,"path":8352285313181715163,"deps":[[12331837146972499874,"equator",false,11170669397063503683]],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/aligned-vec-b67223fcd65d084b/dep-lib-aligned_vec","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
{"rustc":7458672600737419911,"features":"[\"alloc\"]","declared_features":"[\"alloc\", \"default\", \"fresh-rust\", \"nightly\", \"serde\", \"std\"]","target":5388200169723499962,"profile":12994027242049262075,"path":4848795566755010588,"deps":[],"local":[{"CheckDepInfo":{"dep_info":"debug/.fingerprint/allocator-api2-48625379a5c54837/dep-lib-allocator_api2","checksum":false}}],"rustflags":[],"config":8247474407144887393,"compile_kind":0}
@@ -0,0 +1 @@
This file has an mtime of when this was started.
@@ -0,0 +1 @@
b65b14b46b5f3fef

Some files were not shown because too many files have changed in this diff Show More