@@ -590,7 +783,7 @@ function App() {
key={fossflowKey}
initialData={diagramData}
onModelUpdated={handleModelUpdated}
- editorMode="EDITABLE"
+ editorMode={isReadonlyUrl ? 'EXPLORABLE_READONLY' : 'EDITABLE'}
locale={allLocales[i18n.language as keyof typeof allLocales]}
iconPackManager={{
lazyLoadingEnabled: iconPackManager.lazyLoadingEnabled,
@@ -609,28 +802,45 @@ function App() {
{t('dialog.save.title')}
-
-
⚠️ {t('dialog.save.warningTitle')}: {t('dialog.save.warningMessage')}
+
+ ⚠️ {t('dialog.save.warningTitle')}:{' '}
+ {t('dialog.save.warningMessage')}
-
+
setDiagramName(e.target.value)}
- onKeyDown={(e) => e.key === 'Enter' && saveDiagram()}
+ onChange={(e) => {
+ return setDiagramName(e.target.value);
+ }}
+ onKeyDown={(e) => {
+ return e.key === 'Enter' && saveDiagram();
+ }}
autoFocus
/>
-
+
@@ -641,64 +851,100 @@ function App() {
{t('dialog.load.title')}
-
-
⚠️ {t('dialog.load.noteTitle')}: {t('dialog.load.noteMessage')}
+
+ ⚠️ {t('dialog.load.noteTitle')}:{' '}
+ {t('dialog.load.noteMessage')}
{diagrams.length === 0 ? (
{t('dialog.load.noSavedDiagrams')}
) : (
- diagrams.map(diagram => (
-
-
-
{diagram.name}
-
-
{t('dialog.load.updated')}: {new Date(diagram.updatedAt).toLocaleString()}
+ diagrams.map((diagram) => {
+ return (
+
+
+ {diagram.name}
+
+
+ {t('dialog.load.updated')}:{' '}
+ {new Date(diagram.updatedAt).toLocaleString()}
+
+
+
+
+
+
-
-
-
-
-
- ))
+ );
+ })
)}
-
+
)}
-
{/* Export Dialog */}
{showExportDialog && (
{t('dialog.export.title')}
-
+
- ✅ {t('dialog.export.recommendedTitle')}: {t('dialog.export.recommendedMessage')}
+ ✅ {t('dialog.export.recommendedTitle')}:{' '}
+ {t('dialog.export.recommendedMessage')}
{t('dialog.export.noteMessage')}
-
-
+
+
@@ -706,7 +952,11 @@ function App() {
{/* Storage Manager */}
{showStorageManager && (
-
setShowStorageManager(false)} />
+ {
+ return setShowStorageManager(false);
+ }}
+ />
)}
{/* Diagram Manager */}
@@ -715,7 +965,9 @@ function App() {
onLoadDiagram={handleDiagramManagerLoad}
currentDiagramId={currentDiagram?.id}
currentDiagramData={currentModel || diagramData}
- onClose={() => setShowDiagramManager(false)}
+ onClose={() => {
+ return setShowDiagramManager(false);
+ }}
/>
)}
diff --git a/packages/fossflow-app/src/components/DiagramManager.css b/packages/fossflow-app/src/components/DiagramManager.css
index a8a785e..10c9a9d 100644
--- a/packages/fossflow-app/src/components/DiagramManager.css
+++ b/packages/fossflow-app/src/components/DiagramManager.css
@@ -129,6 +129,15 @@
background: #da190b;
}
+.action-button.share {
+ background: #2196f3;
+ color: white;
+}
+
+.action-button.share:hover {
+ background: #0b7dda;
+}
+
.loading {
padding: 40px;
text-align: center;
diff --git a/packages/fossflow-app/src/components/DiagramManager.tsx b/packages/fossflow-app/src/components/DiagramManager.tsx
index dbaac29..89336bd 100644
--- a/packages/fossflow-app/src/components/DiagramManager.tsx
+++ b/packages/fossflow-app/src/components/DiagramManager.tsx
@@ -9,11 +9,11 @@ interface Props {
onClose: () => void;
}
-export const DiagramManager: React.FC
= ({
- onLoadDiagram,
- currentDiagramId,
+export const DiagramManager: React.FC = ({
+ onLoadDiagram,
+ currentDiagramId,
currentDiagramData,
- onClose
+ onClose
}) => {
const [diagrams, setDiagrams] = useState([]);
const [loading, setLoading] = useState(true);
@@ -36,7 +36,9 @@ export const DiagramManager: React.FC = ({
await storageManager.initialize();
const isServer = storageManager.isServerStorage();
setIsServerStorage(isServer);
- console.log(`DiagramManager: Using ${isServer ? 'server' : 'session'} storage`);
+ console.log(
+ `DiagramManager: Using ${isServer ? 'server' : 'session'} storage`
+ );
// Load diagram list
const storage = storageManager.getStorage();
@@ -45,7 +47,8 @@ export const DiagramManager: React.FC = ({
console.log(`DiagramManager: Loaded ${list.length} diagrams`);
setDiagrams(list);
} catch (err) {
- const errorMsg = err instanceof Error ? err.message : 'Failed to load diagrams';
+ const errorMsg =
+ err instanceof Error ? err.message : 'Failed to load diagrams';
console.error('DiagramManager error:', err);
setError(errorMsg);
} finally {
@@ -66,7 +69,9 @@ export const DiagramManager: React.FC = ({
onLoadDiagram(id, data);
// Small delay to ensure parent component finishes state updates
- await new Promise(resolve => setTimeout(resolve, 100));
+ await new Promise((resolve) => {
+ return setTimeout(resolve, 100);
+ });
onClose();
} catch (err) {
@@ -91,6 +96,24 @@ export const DiagramManager: React.FC = ({
}
};
+ const handleCopyShareLink = (id: string) => {
+ const shareUrl = `${window.location.origin}/display/${id}`;
+ navigator.clipboard
+ .writeText(shareUrl)
+ .then(() => {
+ alert(`Share link copied to clipboard:\n${shareUrl}`);
+ })
+ .catch(() => {
+ const textArea = document.createElement('textarea');
+ textArea.value = shareUrl;
+ document.body.appendChild(textArea);
+ textArea.select();
+ document.execCommand('copy');
+ document.body.removeChild(textArea);
+ alert(`Share link copied to clipboard:\n${shareUrl}`);
+ });
+ };
+
const handleSave = async () => {
if (!saveName.trim()) {
setError('Please enter a diagram name');
@@ -101,9 +124,9 @@ export const DiagramManager: React.FC = ({
const storage = storageManager.getStorage();
// Check if a diagram with this name already exists (excluding current diagram)
- const existingDiagram = diagrams.find(d =>
- d.name === saveName.trim() && d.id !== currentDiagramId
- );
+ const existingDiagram = diagrams.find((d) => {
+ return d.name === saveName.trim() && d.id !== currentDiagramId;
+ });
if (existingDiagram) {
const confirmOverwrite = window.confirm(
@@ -132,8 +155,12 @@ export const DiagramManager: React.FC = ({
name: saveName
};
- console.log(`DiagramManager: Saving diagram with ${dataToSave.icons?.length || 0} icons`);
- const importedCount = (dataToSave.icons || []).filter((icon: any) => icon.collection === 'imported').length;
+ console.log(
+ `DiagramManager: Saving diagram with ${dataToSave.icons?.length || 0} icons`
+ );
+ const importedCount = (dataToSave.icons || []).filter((icon: any) => {
+ return icon.collection === 'imported';
+ }).length;
console.log(`DiagramManager: Including ${importedCount} imported icons`);
if (currentDiagramId) {
@@ -157,11 +184,15 @@ export const DiagramManager: React.FC = ({
Diagram Manager
-
+
-
+
{isServerStorage ? '🌐 Server Storage' : '💾 Local Storage'}
{isServerStorage && (
@@ -171,14 +202,10 @@ export const DiagramManager: React.FC = ({
)}
- {error && (
-
- {error}
-
- )}
+ {error &&
{error}
}
-
) : (
- diagrams.map(diagram => (
-
-
-
{diagram.name}
-
- Last modified: {diagram.lastModified.toLocaleString()}
- {diagram.size && ` • ${(diagram.size / 1024).toFixed(1)} KB`}
-
+ diagrams.map((diagram) => {
+ return (
+
+
+
{diagram.name}
+
+ Last modified: {diagram.lastModified.toLocaleString()}
+ {diagram.size &&
+ ` • ${(diagram.size / 1024).toFixed(1)} KB`}
+
+
+
+
+
+
+
-
-
-
-
-
- ))
+ );
+ })
)}
)}
@@ -238,17 +281,27 @@ export const DiagramManager: React.FC
= ({
type="text"
placeholder="Diagram name"
value={saveName}
- onChange={(e) => setSaveName(e.target.value)}
- onKeyDown={(e) => e.key === 'Enter' && handleSave()}
+ onChange={(e) => {
+ return setSaveName(e.target.value);
+ }}
+ onKeyDown={(e) => {
+ return e.key === 'Enter' && handleSave();
+ }}
autoFocus
/>
-
+
)}
);
-};
\ No newline at end of file
+};
diff --git a/packages/fossflow-app/src/i18n.ts b/packages/fossflow-app/src/i18n.ts
index 46f0434..dee001f 100644
--- a/packages/fossflow-app/src/i18n.ts
+++ b/packages/fossflow-app/src/i18n.ts
@@ -11,55 +11,55 @@ i18n
fallbackLng: 'en-US',
debug: process.env.NODE_ENV === 'development',
interpolation: {
- escapeValue: false,
+ escapeValue: false
},
ns: ['app'],
backend: {
- loadPath: './i18n/{{ns}}/{{lng}}.json'
+ loadPath: '/i18n/{{ns}}/{{lng}}.json'
},
detection: {
order: ['localStorage'],
- caches: ['localStorage'],
+ caches: ['localStorage']
}
});
export const supportedLanguages = [
{
label: 'English',
- value: 'en-US',
+ value: 'en-US'
},
{
label: '中文',
- value: 'zh-CN',
+ value: 'zh-CN'
},
{
label: 'Español',
- value: 'es-ES',
+ value: 'es-ES'
},
{
label: 'Português',
- value: 'pt-BR',
+ value: 'pt-BR'
},
{
label: 'Français',
- value: 'fr-FR',
+ value: 'fr-FR'
},
{
label: 'हिन्दी',
- value: 'hi-IN',
+ value: 'hi-IN'
},
{
label: 'বাংলা',
- value: 'bn-BD',
+ value: 'bn-BD'
},
{
label: 'Русский',
- value: 'ru-RU',
+ value: 'ru-RU'
},
{
label: 'Italian',
- value: 'it-IT',
- },
+ value: 'it-IT'
+ }
];
export default i18n;
diff --git a/packages/fossflow-app/src/i18n/bn-BD.json b/packages/fossflow-app/src/i18n/bn-BD.json
index 684e5a0..40f3d1a 100644
--- a/packages/fossflow-app/src/i18n/bn-BD.json
+++ b/packages/fossflow-app/src/i18n/bn-BD.json
@@ -41,6 +41,10 @@
"noteMessage": "রপ্তানি করা JSON ফাইলগুলি পরে আমদানি করা যেতে পারে বা অন্যদের সাথে শেয়ার করা যেতে পারে।",
"btnDownload": "JSON ডাউনলোড করুন",
"btnCancel": "বাতিল করুন"
+ },
+ "readOnly": {
+ "mode": "শুধুমাত্র দেখার মোড",
+ "failed": "ডায়াগ্রাম লোড করতে ব্যর্থ"
}
},
"alert": {
diff --git a/packages/fossflow-app/src/i18n/en-US.json b/packages/fossflow-app/src/i18n/en-US.json
index 1e19a3d..ffce153 100644
--- a/packages/fossflow-app/src/i18n/en-US.json
+++ b/packages/fossflow-app/src/i18n/en-US.json
@@ -41,6 +41,10 @@
"noteMessage": "Exported JSON files can be imported later or shared with others.",
"btnDownload": "Download JSON",
"btnCancel": "Cancel"
+ },
+ "readOnly": {
+ "mode": "View-Only Mode",
+ "failed": "Failed to load diagram"
}
},
"alert": {
diff --git a/packages/fossflow-app/src/i18n/es-ES.json b/packages/fossflow-app/src/i18n/es-ES.json
index b239b00..850cb69 100644
--- a/packages/fossflow-app/src/i18n/es-ES.json
+++ b/packages/fossflow-app/src/i18n/es-ES.json
@@ -41,6 +41,10 @@
"noteMessage": "Los archivos JSON exportados pueden importarse posteriormente o compartirse con otros.",
"btnDownload": "Descargar JSON",
"btnCancel": "Cancelar"
+ },
+ "readOnly": {
+ "mode": "Modo de solo lectura",
+ "failed": "Error al cargar el diagrama"
}
},
"alert": {
diff --git a/packages/fossflow-app/src/i18n/fr-FR.json b/packages/fossflow-app/src/i18n/fr-FR.json
index 1aee5cf..57c72ac 100644
--- a/packages/fossflow-app/src/i18n/fr-FR.json
+++ b/packages/fossflow-app/src/i18n/fr-FR.json
@@ -41,6 +41,10 @@
"noteMessage": "Les fichiers JSON exportés peuvent être importés ultérieurement ou partagés avec d'autres.",
"btnDownload": "Télécharger JSON",
"btnCancel": "Annuler"
+ },
+ "readOnly": {
+ "mode": "Mode lecture seule",
+ "failed": "Échec du chargement du diagramme"
}
},
"alert": {
diff --git a/packages/fossflow-app/src/i18n/hi-IN.json b/packages/fossflow-app/src/i18n/hi-IN.json
index 64a8988..396cb2e 100644
--- a/packages/fossflow-app/src/i18n/hi-IN.json
+++ b/packages/fossflow-app/src/i18n/hi-IN.json
@@ -41,6 +41,10 @@
"noteMessage": "निर्यात की गई JSON फ़ाइलों को बाद में आयात किया जा सकता है या दूसरों के साथ साझा किया जा सकता है।",
"btnDownload": "JSON डाउनलोड करें",
"btnCancel": "रद्द करें"
+ },
+ "readOnly": {
+ "mode": "केवल देखने का मोड",
+ "failed": "आरेख लोड करने में विफल"
}
},
"alert": {
diff --git a/packages/fossflow-app/src/i18n/it-IT.json b/packages/fossflow-app/src/i18n/it-IT.json
index 5efcb67..85b4ef4 100644
--- a/packages/fossflow-app/src/i18n/it-IT.json
+++ b/packages/fossflow-app/src/i18n/it-IT.json
@@ -41,6 +41,10 @@
"noteMessage": "I file JSON esportati possono essere importati in seguito o condivisi con altri.",
"btnDownload": "Scarica JSON",
"btnCancel": "Annulla"
+ },
+ "readOnly": {
+ "mode": "Modalità sola lettura",
+ "failed": "Impossibile caricare il diagramma"
}
},
"alert": {
diff --git a/packages/fossflow-app/src/i18n/pt-BR.json b/packages/fossflow-app/src/i18n/pt-BR.json
index bccc363..cd8f55f 100644
--- a/packages/fossflow-app/src/i18n/pt-BR.json
+++ b/packages/fossflow-app/src/i18n/pt-BR.json
@@ -41,6 +41,10 @@
"noteMessage": "Arquivos JSON exportados podem ser importados posteriormente ou compartilhados com outros.",
"btnDownload": "Baixar JSON",
"btnCancel": "Cancelar"
+ },
+ "readOnly": {
+ "mode": "Modo somente leitura",
+ "failed": "Falha ao carregar diagrama"
}
},
"alert": {
diff --git a/packages/fossflow-app/src/i18n/ru-RU.json b/packages/fossflow-app/src/i18n/ru-RU.json
index 6262e5c..117feeb 100644
--- a/packages/fossflow-app/src/i18n/ru-RU.json
+++ b/packages/fossflow-app/src/i18n/ru-RU.json
@@ -41,6 +41,10 @@
"noteMessage": "Экспортированные файлы JSON можно импортировать позже или поделиться с другими.",
"btnDownload": "Скачать JSON",
"btnCancel": "Отмена"
+ },
+ "readOnly": {
+ "mode": "Режим только для чтения",
+ "failed": "Не удалось загрузить диаграмму"
}
},
"alert": {
diff --git a/packages/fossflow-app/src/i18n/zh-CN.json b/packages/fossflow-app/src/i18n/zh-CN.json
index bcd3008..3916fe1 100644
--- a/packages/fossflow-app/src/i18n/zh-CN.json
+++ b/packages/fossflow-app/src/i18n/zh-CN.json
@@ -41,6 +41,10 @@
"noteMessage": "导出的 JSON 文件可以稍后导入或与他人共享。",
"btnDownload": "下载 JSON",
"btnCancel": "取消"
+ },
+ "readOnly": {
+ "mode": "阅读模式",
+ "failed": "加载图表失败"
}
},
"alert": {