aztomiq 1.0.1 ā 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/bin/aztomiq.js +74 -0
- package/package.json +5 -3
- package/scripts/builds/assets.js +30 -22
- package/scripts/builds/data.js +30 -19
- package/server.js +124 -0
- package/src/assets/css/categories.css +90 -0
- package/src/assets/css/global.css +1815 -0
- package/src/assets/css/home.css +509 -0
- package/src/assets/css/master-layout.css +133 -0
- package/src/assets/css/sidebar-layout.css +264 -0
- package/src/assets/images/logo.svg +29 -0
- package/src/assets/js/global.js +373 -0
- package/src/assets/js/home.js +138 -0
- package/src/assets/js/user-mode.js +54 -0
- package/src/assets/vendor/qrcode/qrcode.min.js +1 -0
- package/src/locales/en/common.yaml +93 -0
- package/src/locales/vi/common.yaml +93 -0
- package/bin/create-aztomiq.js +0 -77
package/README.md
CHANGED
|
@@ -61,7 +61,7 @@ AZtomiq isn't just another static site generator. It's an **Ecosystem** designed
|
|
|
61
61
|
**Option A: Scaffolding a new project (Recommended)**
|
|
62
62
|
|
|
63
63
|
```bash
|
|
64
|
-
npx
|
|
64
|
+
npx aztomiq init my-awesome-app
|
|
65
65
|
cd my-awesome-app
|
|
66
66
|
npm install
|
|
67
67
|
```
|
package/bin/aztomiq.js
CHANGED
|
@@ -270,6 +270,79 @@ meta:
|
|
|
270
270
|
|
|
271
271
|
console.log(`ā
Tool "${name}" created successfully!`);
|
|
272
272
|
console.log(`š Edit it at: src/features/${name}`);
|
|
273
|
+
},
|
|
274
|
+
init: async () => {
|
|
275
|
+
const name = args[1] || 'my-aztomiq-site';
|
|
276
|
+
const projectPath = path.resolve(PROJECT_ROOT, name);
|
|
277
|
+
|
|
278
|
+
if (await fs.pathExists(projectPath)) {
|
|
279
|
+
console.error(`ā Directory "${name}" already exists.`);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
console.log(`š Initializing new AZtomiq project: ${name}...`);
|
|
284
|
+
|
|
285
|
+
// 1. Create structure
|
|
286
|
+
const templateDirs = [
|
|
287
|
+
'src/assets/css',
|
|
288
|
+
'src/assets/js',
|
|
289
|
+
'src/assets/images',
|
|
290
|
+
'src/data',
|
|
291
|
+
'src/features/hello-world/locales',
|
|
292
|
+
'src/includes',
|
|
293
|
+
'src/locales/en',
|
|
294
|
+
'src/locales/vi',
|
|
295
|
+
'src/pages',
|
|
296
|
+
'src/templates'
|
|
297
|
+
];
|
|
298
|
+
|
|
299
|
+
for (const dir of templateDirs) {
|
|
300
|
+
await fs.ensureDir(path.join(projectPath, dir));
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// 2. Create package.json
|
|
304
|
+
const packageJson = {
|
|
305
|
+
name: name,
|
|
306
|
+
version: '1.0.0',
|
|
307
|
+
scripts: {
|
|
308
|
+
"dev": "aztomiq dev",
|
|
309
|
+
"build": "aztomiq build",
|
|
310
|
+
"status": "aztomiq status"
|
|
311
|
+
},
|
|
312
|
+
dependencies: {
|
|
313
|
+
"aztomiq": "latest"
|
|
314
|
+
}
|
|
315
|
+
};
|
|
316
|
+
await fs.writeJson(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
|
|
317
|
+
|
|
318
|
+
// 3. Create global.yaml
|
|
319
|
+
const globalYaml = `site:
|
|
320
|
+
title: "${name}"
|
|
321
|
+
description: "Built with AZtomiq"
|
|
322
|
+
build:
|
|
323
|
+
locales: ["en", "vi"]
|
|
324
|
+
default_locale: "en"
|
|
325
|
+
`;
|
|
326
|
+
await fs.writeFile(path.join(projectPath, 'src/data/global.yaml'), globalYaml);
|
|
327
|
+
|
|
328
|
+
// 4. Create sample tool
|
|
329
|
+
const toolYaml = `id: hello-world
|
|
330
|
+
category: general
|
|
331
|
+
icon: smile
|
|
332
|
+
status: active
|
|
333
|
+
`;
|
|
334
|
+
await fs.writeFile(path.join(projectPath, 'src/features/hello-world/tool.yaml'), toolYaml);
|
|
335
|
+
|
|
336
|
+
const toolEjs = `<h1>Hello World</h1>
|
|
337
|
+
<p>Welcome to your new AZtomiq site!</p>
|
|
338
|
+
`;
|
|
339
|
+
await fs.writeFile(path.join(projectPath, 'src/features/hello-world/index.ejs'), toolEjs);
|
|
340
|
+
|
|
341
|
+
console.log(`\nā
Project "${name}" initialized successfully!`);
|
|
342
|
+
console.log(`š Next steps:`);
|
|
343
|
+
console.log(` cd ${name}`);
|
|
344
|
+
console.log(` npm install`);
|
|
345
|
+
console.log(` npm run dev`);
|
|
273
346
|
}
|
|
274
347
|
};
|
|
275
348
|
|
|
@@ -284,6 +357,7 @@ Commands:
|
|
|
284
357
|
aztomiq dev Start development server (Watcher + Node)
|
|
285
358
|
aztomiq build Build for production (--force, --obfuscate)
|
|
286
359
|
aztomiq deploy Deploy to public distribution repository
|
|
360
|
+
aztomiq init <name> Initialize a new AZtomiq project
|
|
287
361
|
aztomiq status Scan ecosystem health (Locales, Tests, Config)
|
|
288
362
|
aztomiq analyze Analyze tool payloads and ecosystem size
|
|
289
363
|
aztomiq cleanup Remove build artifacts (--drafts to delete draft tools)
|
package/package.json
CHANGED
|
@@ -1,17 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "aztomiq",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "AZtomiq - A comprehensive A-Z multi-tool framework with atomic architecture",
|
|
5
5
|
"main": "scripts/build.js",
|
|
6
6
|
"bin": {
|
|
7
|
-
"aztomiq": "bin/aztomiq.js"
|
|
8
|
-
"create-aztomiq": "bin/create-aztomiq.js"
|
|
7
|
+
"aztomiq": "bin/aztomiq.js"
|
|
9
8
|
},
|
|
10
9
|
"files": [
|
|
11
10
|
"bin",
|
|
12
11
|
"scripts",
|
|
12
|
+
"src/assets",
|
|
13
13
|
"src/includes",
|
|
14
14
|
"src/templates",
|
|
15
|
+
"src/locales",
|
|
16
|
+
"server.js",
|
|
15
17
|
"package.json",
|
|
16
18
|
"README.md",
|
|
17
19
|
"LICENSE"
|
package/scripts/builds/assets.js
CHANGED
|
@@ -82,38 +82,46 @@ async function buildAssets() {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
// 1. Global CSS
|
|
85
|
-
|
|
86
|
-
|
|
85
|
+
const cssDirs = [path.join(paths.CORE_ROOT, 'src/assets/css'), cssSrc].filter(fs.existsSync);
|
|
86
|
+
const cssFiles = {};
|
|
87
|
+
for (const dir of cssDirs) {
|
|
88
|
+
const files = fs.readdirSync(dir);
|
|
87
89
|
for (const file of files) {
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
}
|
|
90
|
+
if (file.endsWith('.css')) cssFiles[file] = path.join(dir, file);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
for (const [file, srcPath] of Object.entries(cssFiles)) {
|
|
95
|
+
const destPath = path.join(cssDist, file);
|
|
96
|
+
if (hasChanged(srcPath) || !fs.existsSync(destPath)) {
|
|
97
|
+
if (isSecure) {
|
|
98
|
+
console.time(`šØ Minifying Global CSS: ${file}`);
|
|
99
|
+
try { execSync(`npx clean-css-cli -o "${destPath}" "${srcPath}"`); }
|
|
100
|
+
catch (e) { await fs.copy(srcPath, destPath); }
|
|
101
|
+
console.timeEnd(`šØ Minifying Global CSS: ${file}`);
|
|
102
|
+
} else {
|
|
103
|
+
console.time(`šØ Copying Global CSS: ${file}`);
|
|
104
|
+
await fs.copy(srcPath, destPath);
|
|
105
|
+
console.timeEnd(`šØ Copying Global CSS: ${file}`);
|
|
102
106
|
}
|
|
103
107
|
}
|
|
104
108
|
}
|
|
105
109
|
|
|
106
110
|
// 2. Global JS
|
|
107
|
-
|
|
108
|
-
|
|
111
|
+
const jsDirs = [path.join(paths.CORE_ROOT, 'src/assets/js'), jsSrc].filter(fs.existsSync);
|
|
112
|
+
const jsFiles = {};
|
|
113
|
+
for (const dir of jsDirs) {
|
|
114
|
+
const files = fs.readdirSync(dir);
|
|
109
115
|
for (const file of files) {
|
|
110
|
-
if (
|
|
111
|
-
const srcPath = path.join(jsSrc, file);
|
|
112
|
-
const destPath = path.join(jsDist, file);
|
|
113
|
-
processJs(srcPath, destPath, file);
|
|
116
|
+
if (file.endsWith('.js')) jsFiles[file] = path.join(dir, file);
|
|
114
117
|
}
|
|
115
118
|
}
|
|
116
119
|
|
|
120
|
+
for (const [file, srcPath] of Object.entries(jsFiles)) {
|
|
121
|
+
const destPath = path.join(jsDist, file);
|
|
122
|
+
processJs(srcPath, destPath, file);
|
|
123
|
+
}
|
|
124
|
+
|
|
117
125
|
// 3. Feature Assets
|
|
118
126
|
if (await fs.pathExists(featuresDir)) {
|
|
119
127
|
const features = await fs.readdir(featuresDir);
|
package/scripts/builds/data.js
CHANGED
|
@@ -44,28 +44,39 @@ function loadLocales(lang) {
|
|
|
44
44
|
const srcDir = paths.SRC;
|
|
45
45
|
|
|
46
46
|
// 1. Load legacy file
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
47
|
+
const legacyPaths = [
|
|
48
|
+
path.join(paths.CORE_ROOT, 'src/locales', `${lang}.json`),
|
|
49
|
+
path.join(srcDir, 'locales', `${lang}.json`)
|
|
50
|
+
];
|
|
51
|
+
for (const p of legacyPaths) {
|
|
52
|
+
if (fs.existsSync(p)) {
|
|
53
|
+
try {
|
|
54
|
+
Object.assign(translations, typeof require(p) === 'string' ? JSON.parse(fs.readFileSync(p, 'utf8')) : require(p));
|
|
55
|
+
} catch (e) { console.error(`Error loading legacy locale ${lang} `, e); }
|
|
56
|
+
}
|
|
52
57
|
}
|
|
53
58
|
|
|
54
59
|
// 2. Load module folders
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
60
|
+
const dirPaths = [
|
|
61
|
+
path.join(paths.CORE_ROOT, 'src/locales', lang),
|
|
62
|
+
path.join(srcDir, 'locales', lang)
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
for (const dirPath of dirPaths) {
|
|
66
|
+
if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
|
|
67
|
+
const files = fs.readdirSync(dirPath);
|
|
68
|
+
for (const file of files) {
|
|
69
|
+
if (file.endsWith('.json')) {
|
|
70
|
+
try {
|
|
71
|
+
const content = JSON.parse(fs.readFileSync(path.join(dirPath, file), 'utf8'));
|
|
72
|
+
Object.assign(translations, content);
|
|
73
|
+
} catch (e) { console.error(`Error loading locale module ${lang}/${file}`, e); }
|
|
74
|
+
} else if (file.endsWith('.yaml') || file.endsWith('.yml')) {
|
|
75
|
+
try {
|
|
76
|
+
const content = yaml.load(fs.readFileSync(path.join(dirPath, file), 'utf8'));
|
|
77
|
+
Object.assign(translations, content);
|
|
78
|
+
} catch (e) { console.error(`Error loading locale module ${lang}/${file}`, e); }
|
|
79
|
+
}
|
|
69
80
|
}
|
|
70
81
|
}
|
|
71
82
|
}
|
package/server.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
|
|
2
|
+
const express = require('express');
|
|
3
|
+
const bodyParser = require('body-parser');
|
|
4
|
+
const cors = require('cors');
|
|
5
|
+
const fs = require('fs-extra');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const yaml = require('js-yaml');
|
|
8
|
+
const { exec } = require('child_process');
|
|
9
|
+
|
|
10
|
+
const app = express();
|
|
11
|
+
const PORT = 3000;
|
|
12
|
+
|
|
13
|
+
// Configuration
|
|
14
|
+
const SRC_DIR = path.join(__dirname, 'src');
|
|
15
|
+
const FEATURES_DIR = path.join(SRC_DIR, 'features');
|
|
16
|
+
const DIST_DIR = path.join(__dirname, 'dist-dev');
|
|
17
|
+
|
|
18
|
+
app.use(cors());
|
|
19
|
+
app.use(bodyParser.json());
|
|
20
|
+
|
|
21
|
+
// 1. Static Files (Serve the site)
|
|
22
|
+
app.use(express.static(DIST_DIR));
|
|
23
|
+
|
|
24
|
+
// 2. Admin API
|
|
25
|
+
// GET /api/features - List all features and their configs
|
|
26
|
+
app.get('/api/features', (req, res) => {
|
|
27
|
+
try {
|
|
28
|
+
const features = fs.readdirSync(FEATURES_DIR).filter(f => {
|
|
29
|
+
return fs.statSync(path.join(FEATURES_DIR, f)).isDirectory();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const data = features.map(feature => {
|
|
33
|
+
const configPath = path.join(FEATURES_DIR, feature, 'tool.yaml');
|
|
34
|
+
if (fs.existsSync(configPath)) {
|
|
35
|
+
const config = yaml.load(fs.readFileSync(configPath, 'utf8'));
|
|
36
|
+
return { id: feature, config };
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}).filter(item => item !== null);
|
|
40
|
+
|
|
41
|
+
res.json(data);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
res.status(500).json({ error: err.message });
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// POST /api/features/:id - Update feature config
|
|
48
|
+
app.post('/api/features/:id', (req, res) => {
|
|
49
|
+
const { id } = req.params;
|
|
50
|
+
const newConfig = req.body;
|
|
51
|
+
const configPath = path.join(FEATURES_DIR, id, 'tool.yaml');
|
|
52
|
+
|
|
53
|
+
if (!fs.existsSync(configPath)) {
|
|
54
|
+
return res.status(404).json({ error: 'Feature not found' });
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
fs.writeFileSync(configPath, yaml.dump(newConfig), 'utf8');
|
|
59
|
+
console.log(`š Updated config for ${id}`);
|
|
60
|
+
|
|
61
|
+
console.log('š Triggering rebuild...');
|
|
62
|
+
exec('node scripts/build.js', (error, stdout, stderr) => {
|
|
63
|
+
if (error) console.error(`Build error: ${error}`);
|
|
64
|
+
else console.log(`ā
Build complete`);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
res.json({ success: true, message: 'Config updated and rebuild triggered' });
|
|
68
|
+
} catch (err) {
|
|
69
|
+
res.status(500).json({ error: err.message });
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// GET /api/global
|
|
74
|
+
app.get('/api/global', (req, res) => {
|
|
75
|
+
try {
|
|
76
|
+
const globalPath = path.join(SRC_DIR, 'data', 'global.yaml');
|
|
77
|
+
if (fs.existsSync(globalPath)) {
|
|
78
|
+
const config = yaml.load(fs.readFileSync(globalPath, 'utf8'));
|
|
79
|
+
res.json(config);
|
|
80
|
+
} else {
|
|
81
|
+
res.json({});
|
|
82
|
+
}
|
|
83
|
+
} catch (err) {
|
|
84
|
+
res.status(500).json({ error: err.message });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// POST /api/global
|
|
89
|
+
app.post('/api/global', (req, res) => {
|
|
90
|
+
try {
|
|
91
|
+
const globalPath = path.join(SRC_DIR, 'data', 'global.yaml');
|
|
92
|
+
const newConfig = req.body;
|
|
93
|
+
|
|
94
|
+
fs.writeFileSync(globalPath, yaml.dump(newConfig), 'utf8');
|
|
95
|
+
console.log(`š Updated Global Config`);
|
|
96
|
+
|
|
97
|
+
console.log('š Triggering rebuild...');
|
|
98
|
+
exec('node scripts/build.js', (error, stdout, stderr) => {
|
|
99
|
+
if (error) console.error(`Build error: ${error}`);
|
|
100
|
+
else console.log(`ā
Build complete`);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
res.json({ success: true, message: 'Global config updated' });
|
|
104
|
+
} catch (err) {
|
|
105
|
+
res.status(500).json({ error: err.message });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Fallback to index.html for SPA routing (only for page requests, not assets)
|
|
110
|
+
app.use((req, res) => {
|
|
111
|
+
// If request has an extension, it's likely a missing asset
|
|
112
|
+
if (path.extname(req.path)) {
|
|
113
|
+
return res.status(404).send('Not found');
|
|
114
|
+
}
|
|
115
|
+
res.sendFile(path.join(DIST_DIR, 'index.html'));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
app.listen(PORT, () => {
|
|
119
|
+
console.log(`
|
|
120
|
+
š Admin Server running at http://localhost:${PORT}/admin
|
|
121
|
+
š Admin API: http://localhost:${PORT}/api/features
|
|
122
|
+
š Website: http://localhost:${PORT}/
|
|
123
|
+
`);
|
|
124
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/* Masonry Grid Layout */
|
|
2
|
+
.categories-grid {
|
|
3
|
+
column-count: 2;
|
|
4
|
+
column-gap: 2rem;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
@media (max-width: 900px) {
|
|
8
|
+
.categories-grid {
|
|
9
|
+
column-count: 1;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
.category-card {
|
|
14
|
+
break-inside: avoid; /* Prevent card splitting */
|
|
15
|
+
margin-bottom: 2rem;
|
|
16
|
+
padding: 1.5rem;
|
|
17
|
+
background: var(--card-bg);
|
|
18
|
+
border: 1px solid var(--border-color);
|
|
19
|
+
border-radius: 12px;
|
|
20
|
+
box-shadow: var(--shadow);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.category-title {
|
|
24
|
+
font-size: 1.25rem;
|
|
25
|
+
margin-bottom: 1.5rem;
|
|
26
|
+
display: flex;
|
|
27
|
+
align-items: center;
|
|
28
|
+
gap: 0.75rem;
|
|
29
|
+
border-bottom: 1px solid var(--border-color);
|
|
30
|
+
padding-bottom: 1rem;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.cat-icon {
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
width: 36px;
|
|
38
|
+
height: 36px;
|
|
39
|
+
background: var(--bg-hover);
|
|
40
|
+
border-radius: 8px;
|
|
41
|
+
color: var(--primary-color);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
.cat-count {
|
|
45
|
+
margin-left: auto;
|
|
46
|
+
font-size: 0.8rem;
|
|
47
|
+
background: var(--bg-hover);
|
|
48
|
+
padding: 0.2rem 0.6rem;
|
|
49
|
+
border-radius: 20px;
|
|
50
|
+
color: var(--text-muted);
|
|
51
|
+
white-space: nowrap;
|
|
52
|
+
display: inline-flex;
|
|
53
|
+
align-items: center;
|
|
54
|
+
border: 1px solid var(--border-color);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.cat-tools-list {
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: column;
|
|
60
|
+
gap: 0.5rem;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.cat-tool-item {
|
|
64
|
+
display: flex;
|
|
65
|
+
align-items: center;
|
|
66
|
+
gap: 0.75rem;
|
|
67
|
+
padding: 0.6rem 0.8rem;
|
|
68
|
+
border-radius: 8px;
|
|
69
|
+
color: var(--text-color); /* Updated text color */
|
|
70
|
+
transition: all 0.2s;
|
|
71
|
+
border: 1px solid transparent; /* Prevent layout shift on hover border */
|
|
72
|
+
position: relative;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
.cat-tool-item:hover {
|
|
76
|
+
background: var(--bg-hover);
|
|
77
|
+
color: var(--primary-color);
|
|
78
|
+
transform: translateX(5px);
|
|
79
|
+
border-color: var(--border-color);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.tool-icon {
|
|
83
|
+
color: var(--text-muted);
|
|
84
|
+
display: flex;
|
|
85
|
+
align-items: center;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.cat-tool-item:hover .tool-icon {
|
|
89
|
+
color: var(--primary-color);
|
|
90
|
+
}
|