+
SpiralDB
+
Dein Kontakt-Netzwerk als Pixel-Spirale
+
+
+
+
+
+
+ {#if spiralStore.stats}
+
+ Statistiken
+
+
+ {spiralStore.stats.imageSize}x{spiralStore.stats.imageSize}
+ Bildgröße
+
+
+ {spiralStore.stats.activeRecords}
+ Kontakte
+
+
+ {spiralStore.stats.usedPixels}
+ Pixel belegt
+
+
+ {spiralStore.stats.compressionRatio}%
+ Kompression vs JSON
+
+
+
+
+ {#each Object.entries(COLORS) as [idx, color]}
+
+
+ {color.name}
+ {color.bits.join('')}
+
+ {/each}
+
+
+ {/if}
+
+
+
+
+ Gespeicherte Kontakte
+ {#if spiralStore.records.length > 0}
+ {spiralStore.records.length}
+ {/if}
+
+
+ {#if spiralStore.records.length === 0}
+ Noch keine Kontakte in der Spirale.
+ {:else}
+
+ {#each spiralStore.records as record}
+
+
+
+ {#if record.data.company}
+ {record.data.company}
+ {/if}
+ {#if record.data.city}
+ {record.data.city}
+ {/if}
+
+ {#if record.data.hasEmail}
+ @
+ {/if}
+ {#if record.data.hasPhone}
+ 📞
+ {/if}
+
+
+
+
+ {/each}
+
+ {/if}
+
+
+
+
+ Aktionen
+
+ spiralStore.downloadPng()}
+ disabled={!spiralStore.stats || spiralStore.stats.totalRecords === 0}
+ >
+ PNG herunterladen
+
+ PNG importieren
+
+ Kontakte neu importieren ({contactsStore.contacts.length})
+
+ spiralStore.clear()}
+ disabled={!spiralStore.stats || spiralStore.stats.totalRecords === 0}
+ >
+ Alles löschen
+
+
+
+
+
+ SpiralDB kodiert deine Kontakte als farbige Pixel in einem Spiralmuster. Jedes
+ Pixel speichert 3 Bit (8 Farben). Das Bild wächst von der Mitte nach außen, je mehr
+ Kontakte du sammelst. Favoriten werden mit einem Stern markiert.
+
+
+
+
+
+
+
+
+
diff --git a/packages/spiral-db/src/index.ts b/packages/spiral-db/src/index.ts
index df7c2f598..51892cc67 100644
--- a/packages/spiral-db/src/index.ts
+++ b/packages/spiral-db/src/index.ts
@@ -93,6 +93,7 @@ export {
export {
createTodoSchema,
createQuoteSchema,
+ createContactSchema,
encodeSchema,
decodeSchema,
getSchemaPixelCount,
diff --git a/packages/spiral-db/src/schema.test.ts b/packages/spiral-db/src/schema.test.ts
index a49bcc819..7eb5825fd 100644
--- a/packages/spiral-db/src/schema.test.ts
+++ b/packages/spiral-db/src/schema.test.ts
@@ -9,6 +9,7 @@ import {
getSchemaPixelCount,
createTodoSchema,
createQuoteSchema,
+ createContactSchema,
validateRecord,
getFieldNames,
} from './schema.js';
@@ -240,6 +241,47 @@ describe('Quote Schema', () => {
});
});
+describe('Contact Schema', () => {
+ it('should create contact schema with correct fields', () => {
+ const schema = createContactSchema();
+ expect(schema.name).toBe('contact');
+ expect(schema.fields).toHaveLength(8);
+ expect(schema.fields.map((f) => f.name)).toEqual([
+ 'id',
+ 'status',
+ 'hasEmail',
+ 'hasPhone',
+ 'createdAt',
+ 'name',
+ 'company',
+ 'city',
+ ]);
+ });
+
+ it('should validate a valid contact record', () => {
+ const schema = createContactSchema();
+ const result = validateRecord(schema, {
+ id: 0,
+ status: 0,
+ hasEmail: true,
+ hasPhone: false,
+ createdAt: new Date(),
+ name: 'Max Mustermann',
+ company: null,
+ city: null,
+ });
+ expect(result.valid).toBe(true);
+ });
+
+ it('should mark company and city as nullable', () => {
+ const schema = createContactSchema();
+ const companyField = schema.fields.find((f) => f.name === 'company');
+ const cityField = schema.fields.find((f) => f.name === 'city');
+ expect(companyField?.nullable).toBe(true);
+ expect(cityField?.nullable).toBe(true);
+ });
+});
+
describe('getFieldNames', () => {
it('should return field names in order', () => {
const schema = createTodoSchema();
diff --git a/packages/spiral-db/src/schema.ts b/packages/spiral-db/src/schema.ts
index 49c9bfcd2..75266a93b 100644
--- a/packages/spiral-db/src/schema.ts
+++ b/packages/spiral-db/src/schema.ts
@@ -110,6 +110,26 @@ export function createTodoSchema(): SchemaDefinition {
};
}
+/**
+ * Create a schema for Contact items (Contacts app)
+ */
+export function createContactSchema(): SchemaDefinition {
+ return {
+ version: 1,
+ name: 'contact',
+ fields: [
+ { name: 'id', type: 'int', maxLength: 12 }, // 0-4095
+ { name: 'status', type: 'int', maxLength: 3 }, // 0=active, 2=favorite, 4=archived
+ { name: 'hasEmail', type: 'bool', maxLength: 1 },
+ { name: 'hasPhone', type: 'bool', maxLength: 1 },
+ { name: 'createdAt', type: 'timestamp', maxLength: 24 },
+ { name: 'name', type: 'string', maxLength: 100 },
+ { name: 'company', type: 'string', maxLength: 100, nullable: true },
+ { name: 'city', type: 'string', maxLength: 50, nullable: true },
+ ],
+ };
+}
+
/**
* Create a schema for Quote items (Zitare app)
*/