@takimcizgisi/ajan 0.2.5
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/LICENSE +674 -0
- package/README.md +366 -0
- package/config/models.json +54 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +741 -0
- package/dist/cli/tui.d.ts +145 -0
- package/dist/cli/tui.js +1003 -0
- package/dist/config/configManager.d.ts +18 -0
- package/dist/config/configManager.js +204 -0
- package/dist/config/types.d.ts +98 -0
- package/dist/config/types.js +2 -0
- package/dist/core/agent.d.ts +88 -0
- package/dist/core/agent.js +222 -0
- package/dist/electron/main.d.ts +1 -0
- package/dist/electron/main.js +364 -0
- package/dist/electron/preload.d.ts +1 -0
- package/dist/electron/preload.js +67 -0
- package/dist/engine/llama.d.ts +39 -0
- package/dist/engine/llama.js +146 -0
- package/dist/engine/modelManager.d.ts +18 -0
- package/dist/engine/modelManager.js +73 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +9 -0
- package/dist/service.d.ts +90 -0
- package/dist/service.js +268 -0
- package/dist/tools/data.d.ts +3 -0
- package/dist/tools/data.js +111 -0
- package/dist/tools/edit.d.ts +2 -0
- package/dist/tools/edit.js +48 -0
- package/dist/tools/file.d.ts +6 -0
- package/dist/tools/file.js +274 -0
- package/dist/tools/filesys.d.ts +3 -0
- package/dist/tools/filesys.js +74 -0
- package/dist/tools/memory.d.ts +3 -0
- package/dist/tools/memory.js +69 -0
- package/dist/tools/patch.d.ts +15 -0
- package/dist/tools/patch.js +129 -0
- package/dist/tools/registry.d.ts +10 -0
- package/dist/tools/registry.js +64 -0
- package/dist/tools/taskComplete.d.ts +2 -0
- package/dist/tools/taskComplete.js +19 -0
- package/dist/tools/terminal.d.ts +2 -0
- package/dist/tools/terminal.js +130 -0
- package/dist/tools/utils.d.ts +7 -0
- package/dist/tools/utils.js +63 -0
- package/dist/tools/web.d.ts +8 -0
- package/dist/tools/web.js +181 -0
- package/dist/utils/clipboard.d.ts +2 -0
- package/dist/utils/clipboard.js +40 -0
- package/dist/utils/lock.d.ts +12 -0
- package/dist/utils/lock.js +68 -0
- package/dist/utils/paths.d.ts +13 -0
- package/dist/utils/paths.js +58 -0
- package/dist/utils/projectContext.d.ts +2 -0
- package/dist/utils/projectContext.js +24 -0
- package/dist/utils/sacGuard.d.ts +4 -0
- package/dist/utils/sacGuard.js +47 -0
- package/dist/utils/sessions.d.ts +29 -0
- package/dist/utils/sessions.js +64 -0
- package/dist/utils/spinner.d.ts +10 -0
- package/dist/utils/spinner.js +32 -0
- package/package.json +106 -0
- package/src/gui/assets/image/AJAN_LOGO.svg +1 -0
- package/src/gui/assets/image/ajan.png +0 -0
- package/src/gui/assets/image/logo.svg +1 -0
- package/src/gui/assets/image/opencode.svg +18 -0
- package/src/gui/css/style.css +430 -0
- package/src/gui/html/chat.html +34 -0
- package/src/gui/html/downloads.html +11 -0
- package/src/gui/html/hakkinda.html +26 -0
- package/src/gui/html/index.html +82 -0
- package/src/gui/html/projects.html +12 -0
- package/src/gui/html/settings.html +4 -0
- package/src/gui/js/app.js +197 -0
- package/src/gui/js/chat.js +434 -0
- package/src/gui/js/downloads.js +172 -0
- package/src/gui/js/hakkinda.js +16 -0
- package/src/gui/js/projects.js +113 -0
- package/src/gui/js/settings.js +177 -0
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { ChatHistoryItem } from "node-llama-cpp";
|
|
2
|
+
export type StoredMessage = {
|
|
3
|
+
kind: "user" | "assistant" | "thought" | "info" | "tool";
|
|
4
|
+
text?: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
state?: "running" | "done" | "error";
|
|
7
|
+
startedAt?: number;
|
|
8
|
+
elapsed?: number;
|
|
9
|
+
output?: string;
|
|
10
|
+
};
|
|
11
|
+
export type TodoItem = {
|
|
12
|
+
text: string;
|
|
13
|
+
done: boolean;
|
|
14
|
+
};
|
|
15
|
+
export type StoredSession = {
|
|
16
|
+
id: string;
|
|
17
|
+
title: string;
|
|
18
|
+
mode: string;
|
|
19
|
+
createdAt: string;
|
|
20
|
+
updatedAt: string;
|
|
21
|
+
history: ChatHistoryItem[];
|
|
22
|
+
messages: StoredMessage[];
|
|
23
|
+
todos?: TodoItem[];
|
|
24
|
+
};
|
|
25
|
+
export declare function generateSessionId(date?: Date): string;
|
|
26
|
+
export declare function saveSession(session: StoredSession): Promise<void>;
|
|
27
|
+
export declare function loadSession(id: string): Promise<StoredSession | null>;
|
|
28
|
+
export declare function deleteSession(id: string): Promise<void>;
|
|
29
|
+
export declare function listSessions(): Promise<StoredSession[]>;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { promises as fs } from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
const ID_RE = /^[A-Za-z0-9._-]+$/;
|
|
5
|
+
function sessionsDir() {
|
|
6
|
+
return path.join(os.homedir(), ".ajan", "sessions");
|
|
7
|
+
}
|
|
8
|
+
function sessionPath(id) {
|
|
9
|
+
return path.join(sessionsDir(), `${id}.json`);
|
|
10
|
+
}
|
|
11
|
+
function isValidId(id) {
|
|
12
|
+
return ID_RE.test(id) && !id.includes("..");
|
|
13
|
+
}
|
|
14
|
+
export function generateSessionId(date = new Date()) {
|
|
15
|
+
const p = (n) => String(n).padStart(2, "0");
|
|
16
|
+
return `${date.getFullYear()}${p(date.getMonth() + 1)}${p(date.getDate())}-${p(date.getHours())}${p(date.getMinutes())}${p(date.getSeconds())}`;
|
|
17
|
+
}
|
|
18
|
+
export async function saveSession(session) {
|
|
19
|
+
await fs.mkdir(sessionsDir(), { recursive: true });
|
|
20
|
+
await fs.writeFile(sessionPath(session.id), JSON.stringify(session, null, 2), "utf8");
|
|
21
|
+
}
|
|
22
|
+
export async function loadSession(id) {
|
|
23
|
+
if (!isValidId(id))
|
|
24
|
+
return null;
|
|
25
|
+
try {
|
|
26
|
+
const raw = await fs.readFile(sessionPath(id), "utf8");
|
|
27
|
+
const data = JSON.parse(raw);
|
|
28
|
+
if (!data || typeof data !== "object" || typeof data.id !== "string")
|
|
29
|
+
return null;
|
|
30
|
+
return data;
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function deleteSession(id) {
|
|
37
|
+
if (!isValidId(id))
|
|
38
|
+
return;
|
|
39
|
+
try {
|
|
40
|
+
await fs.rm(sessionPath(id), { force: true });
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
/* yok say */
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export async function listSessions() {
|
|
47
|
+
try {
|
|
48
|
+
const entries = await fs.readdir(sessionsDir());
|
|
49
|
+
const sessions = [];
|
|
50
|
+
for (const entry of entries) {
|
|
51
|
+
if (!entry.endsWith(".json"))
|
|
52
|
+
continue;
|
|
53
|
+
const data = await loadSession(entry.slice(0, -5));
|
|
54
|
+
if (data)
|
|
55
|
+
sessions.push(data);
|
|
56
|
+
}
|
|
57
|
+
sessions.sort((a, b) => (a.updatedAt < b.updatedAt ? 1 : -1));
|
|
58
|
+
return sessions;
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
return [];
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=sessions.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare class Spinner {
|
|
2
|
+
private readonly interval;
|
|
3
|
+
private frame;
|
|
4
|
+
readonly startedAt: number;
|
|
5
|
+
private readonly text;
|
|
6
|
+
constructor(text: string);
|
|
7
|
+
private render;
|
|
8
|
+
/** Spinner'ı durdurur; finalText verilirse satırı o metinle bitirir, verilmezse satırı temizler. */
|
|
9
|
+
finish(finalText?: string): void;
|
|
10
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export class Spinner {
|
|
2
|
+
interval;
|
|
3
|
+
frame = 0;
|
|
4
|
+
startedAt;
|
|
5
|
+
text;
|
|
6
|
+
constructor(text) {
|
|
7
|
+
this.text = text;
|
|
8
|
+
this.startedAt = Date.now();
|
|
9
|
+
process.stdout.write(`${text} `);
|
|
10
|
+
this.interval = setInterval(() => this.render(), 120);
|
|
11
|
+
}
|
|
12
|
+
render() {
|
|
13
|
+
const width = 8;
|
|
14
|
+
const fill = this.frame % (width + 1);
|
|
15
|
+
const bar = fill === width
|
|
16
|
+
? `[${"=".repeat(width)}]`
|
|
17
|
+
: `[${"=".repeat(fill)}>${" ".repeat(width - fill - 1)}]`;
|
|
18
|
+
this.frame++;
|
|
19
|
+
process.stdout.write(`\r${this.text} ${bar}`);
|
|
20
|
+
}
|
|
21
|
+
/** Spinner'ı durdurur; finalText verilirse satırı o metinle bitirir, verilmezse satırı temizler. */
|
|
22
|
+
finish(finalText) {
|
|
23
|
+
clearInterval(this.interval);
|
|
24
|
+
if (finalText) {
|
|
25
|
+
process.stdout.write(`\r${finalText}\n`);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
process.stdout.write("\r\x1b[2K");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=spinner.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@takimcizgisi/ajan",
|
|
3
|
+
"version": "0.2.5",
|
|
4
|
+
"description": "AJAN AI - Otonom yerel CLI AI kodlama ajanı (Gemma 4 E2B + node-llama-cpp)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "GPL-3.0",
|
|
7
|
+
"author": "TakimCizgisi Yazilim Gelistirme Grubu",
|
|
8
|
+
"keywords": [
|
|
9
|
+
"ai",
|
|
10
|
+
"llm",
|
|
11
|
+
"local",
|
|
12
|
+
"agent",
|
|
13
|
+
"cli",
|
|
14
|
+
"coding-agent",
|
|
15
|
+
"llama-cpp",
|
|
16
|
+
"gemma",
|
|
17
|
+
"cuda"
|
|
18
|
+
],
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "https://github.com/takimcizgisi/ajan.git"
|
|
22
|
+
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"ajan": "./dist/cli/index.js"
|
|
25
|
+
},
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"import": "./dist/index.js",
|
|
31
|
+
"types": "./dist/index.d.ts"
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"dist",
|
|
36
|
+
"!dist/**/*.map",
|
|
37
|
+
"config",
|
|
38
|
+
"src/gui/html",
|
|
39
|
+
"src/gui/css",
|
|
40
|
+
"src/gui/js",
|
|
41
|
+
"src/gui/assets/image",
|
|
42
|
+
"README.md"
|
|
43
|
+
],
|
|
44
|
+
"scripts": {
|
|
45
|
+
"build": "tsc -p tsconfig.json",
|
|
46
|
+
"build:gui": "node -e \"require('fs').rmSync('src/gui/js',{recursive:true,force:true});require('fs').mkdirSync('src/gui/js',{recursive:true})\" && tsc -p src/gui/tsconfig.gui.json",
|
|
47
|
+
"build:electron": "tsc -p tsconfig.electron.json --noEmit",
|
|
48
|
+
"build:all": "npm run build && npm run build:gui",
|
|
49
|
+
"clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
50
|
+
"dev": "tsx src/cli/index.ts",
|
|
51
|
+
"dev:gui": "npm run build:all && electron dist/electron/main.js --dev",
|
|
52
|
+
"gui": "npm run build:all && electron dist/electron/main.js",
|
|
53
|
+
"start": "node dist/cli/index.js",
|
|
54
|
+
"test:tools": "tsx scripts/smoke-tools.ts",
|
|
55
|
+
"release": "node scripts/release.mjs",
|
|
56
|
+
"release:patch": "npm version patch && node scripts/release.mjs",
|
|
57
|
+
"prepack": "npm run clean && npm run build:all"
|
|
58
|
+
},
|
|
59
|
+
"engines": {
|
|
60
|
+
"node": ">=18.0.0"
|
|
61
|
+
},
|
|
62
|
+
"dependencies": {
|
|
63
|
+
"adm-zip": "^0.6.0",
|
|
64
|
+
"chalk": "^5.6.2",
|
|
65
|
+
"diff": "^9.0.0",
|
|
66
|
+
"js-yaml": "^5.3.0",
|
|
67
|
+
"node-llama-cpp": "^3.19.1"
|
|
68
|
+
},
|
|
69
|
+
"devDependencies": {
|
|
70
|
+
"@types/adm-zip": "^0.5.8",
|
|
71
|
+
"@types/diff": "^7.0.2",
|
|
72
|
+
"@types/js-yaml": "^4.0.9",
|
|
73
|
+
"@types/node": "^24.13.3",
|
|
74
|
+
"electron": "^35.7.5",
|
|
75
|
+
"electron-builder": "^26.0.0",
|
|
76
|
+
"tsx": "^4.23.12",
|
|
77
|
+
"typescript": "^7.0.2"
|
|
78
|
+
},
|
|
79
|
+
"build": {
|
|
80
|
+
"appId": "com.takimcizgisi.ajan",
|
|
81
|
+
"productName": "AJAN AI",
|
|
82
|
+
"directories": {
|
|
83
|
+
"output": "release"
|
|
84
|
+
},
|
|
85
|
+
"files": [
|
|
86
|
+
"dist/**/*",
|
|
87
|
+
"src/gui/**/*",
|
|
88
|
+
"config/**/*"
|
|
89
|
+
],
|
|
90
|
+
"extraResources": [
|
|
91
|
+
{
|
|
92
|
+
"from": "config",
|
|
93
|
+
"to": "config"
|
|
94
|
+
}
|
|
95
|
+
],
|
|
96
|
+
"win": {
|
|
97
|
+
"target": "nsis",
|
|
98
|
+
"icon": "src/gui/assets/image/ajan.png"
|
|
99
|
+
},
|
|
100
|
+
"nsis": {
|
|
101
|
+
"oneClick": false,
|
|
102
|
+
"allowToChangeInstallationDirectory": true,
|
|
103
|
+
"createDesktopShortcut": true
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500" zoomAndPan="magnify" viewBox="0 0 375 175.5" height="234" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><g/><clipPath id="ea41dcbb7a"><path d="M 0.53125 36 L 98 36 L 98 138 L 0.53125 138 Z M 0.53125 36 " clip-rule="nonzero"/></clipPath></defs><g clip-path="url(#ea41dcbb7a)"><g fill="#ffffff" fill-opacity="1"><g transform="translate(0.532671, 137.206418)"><g><path d="M 83.25 0 L 71.765625 -29.390625 L 25.96875 -29.390625 L 14.40625 0 L 0.28125 0 L 41.3125 -100.515625 L 56.78125 -100.515625 L 97.171875 0 Z M 48.875 -90.25 L 48.234375 -88.25 C 47.035156 -84.300781 45.273438 -79.238281 42.953125 -73.0625 L 30.109375 -40.015625 L 67.703125 -40.015625 L 54.796875 -73.203125 C 53.460938 -76.484375 52.128906 -80.191406 50.796875 -84.328125 Z M 48.875 -90.25 "/></g></g></g></g><g fill="#ffffff" fill-opacity="1"><g transform="translate(97.982415, 137.206418)"><g><path d="M 32.609375 1.421875 C 15.578125 1.421875 5.46875 -7.375 2.28125 -24.96875 L 15.625 -27.1875 C 16.476562 -21.664062 18.398438 -17.359375 21.390625 -14.265625 C 24.390625 -11.171875 28.148438 -9.625 32.671875 -9.625 C 37.617188 -9.625 41.519531 -11.320312 44.375 -14.71875 C 47.226562 -18.125 48.65625 -23.109375 48.65625 -29.671875 L 48.65625 -89.390625 L 29.328125 -89.390625 L 29.328125 -100.515625 L 62.203125 -100.515625 L 62.203125 -29.96875 C 62.203125 -20.21875 59.5625 -12.546875 54.28125 -6.953125 C 49.007812 -1.367188 41.785156 1.421875 32.609375 1.421875 Z M 32.609375 1.421875 "/></g></g></g><g fill="#ffffff" fill-opacity="1"><g transform="translate(171.034053, 137.206418)"><g><path d="M 83.25 0 L 71.765625 -29.390625 L 25.96875 -29.390625 L 14.40625 0 L 0.28125 0 L 41.3125 -100.515625 L 56.78125 -100.515625 L 97.171875 0 Z M 48.875 -90.25 L 48.234375 -88.25 C 47.035156 -84.300781 45.273438 -79.238281 42.953125 -73.0625 L 30.109375 -40.015625 L 67.703125 -40.015625 L 54.796875 -73.203125 C 53.460938 -76.484375 52.128906 -80.191406 50.796875 -84.328125 Z M 48.875 -90.25 "/></g></g></g><g fill="#ffffff" fill-opacity="1"><g transform="translate(268.483797, 137.206418)"><g><path d="M 77.1875 0 L 23.40625 -85.609375 L 23.75 -78.6875 L 24.109375 -66.78125 L 24.109375 0 L 11.984375 0 L 11.984375 -100.515625 L 27.828125 -100.515625 L 82.1875 -14.34375 C 81.613281 -23.664062 81.328125 -30.414062 81.328125 -34.59375 L 81.328125 -100.515625 L 93.59375 -100.515625 L 93.59375 0 Z M 77.1875 0 "/></g></g></g></svg>
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="500" zoomAndPan="magnify" viewBox="0 0 375 175.5" height="234" preserveAspectRatio="xMidYMid meet" version="1.0"><defs><g/><clipPath id="5de57a9a76"><path d="M 0.53125 5 L 374.464844 5 L 374.464844 170 L 0.53125 170 Z M 0.53125 5 " clip-rule="nonzero"/></clipPath><clipPath id="5f2663e074"><rect x="0" width="375" y="0" height="165"/></clipPath></defs><g clip-path="url(#5de57a9a76)"><g transform="matrix(1, 0, 0, 1, 0.000000000000000111, 5)"><g clip-path="url(#5f2663e074)"><g fill="#ffffff" fill-opacity="1"><g transform="translate(0.758612, 132.097827)"><g><path d="M 83.25 0 L 71.765625 -29.390625 L 25.96875 -29.390625 L 14.40625 0 L 0.28125 0 L 41.3125 -100.515625 L 56.78125 -100.515625 L 97.171875 0 Z M 48.875 -90.25 L 48.234375 -88.25 C 47.035156 -84.300781 45.273438 -79.238281 42.953125 -73.0625 L 30.109375 -40.015625 L 67.703125 -40.015625 L 54.796875 -73.203125 C 53.460938 -76.484375 52.128906 -80.191406 50.796875 -84.328125 Z M 48.875 -90.25 "/></g></g></g><g fill="#ffffff" fill-opacity="1"><g transform="translate(98.207384, 132.097827)"><g><path d="M 32.609375 1.421875 C 15.578125 1.421875 5.46875 -7.375 2.28125 -24.96875 L 15.625 -27.1875 C 16.476562 -21.664062 18.398438 -17.359375 21.390625 -14.265625 C 24.390625 -11.171875 28.148438 -9.625 32.671875 -9.625 C 37.617188 -9.625 41.519531 -11.320312 44.375 -14.71875 C 47.226562 -18.125 48.65625 -23.109375 48.65625 -29.671875 L 48.65625 -89.390625 L 29.328125 -89.390625 L 29.328125 -100.515625 L 62.203125 -100.515625 L 62.203125 -29.96875 C 62.203125 -20.21875 59.5625 -12.546875 54.28125 -6.953125 C 49.007812 -1.367188 41.785156 1.421875 32.609375 1.421875 Z M 32.609375 1.421875 "/></g></g></g><g fill="#ffffff" fill-opacity="1"><g transform="translate(171.258295, 132.097827)"><g><path d="M 83.25 0 L 71.765625 -29.390625 L 25.96875 -29.390625 L 14.40625 0 L 0.28125 0 L 41.3125 -100.515625 L 56.78125 -100.515625 L 97.171875 0 Z M 48.875 -90.25 L 48.234375 -88.25 C 47.035156 -84.300781 45.273438 -79.238281 42.953125 -73.0625 L 30.109375 -40.015625 L 67.703125 -40.015625 L 54.796875 -73.203125 C 53.460938 -76.484375 52.128906 -80.191406 50.796875 -84.328125 Z M 48.875 -90.25 "/></g></g></g><g fill="#ffffff" fill-opacity="1"><g transform="translate(268.707067, 132.097827)"><g><path d="M 77.1875 0 L 23.40625 -85.609375 L 23.75 -78.6875 L 24.109375 -66.78125 L 24.109375 0 L 11.984375 0 L 11.984375 -100.515625 L 27.828125 -100.515625 L 82.1875 -14.34375 C 81.613281 -23.664062 81.328125 -30.414062 81.328125 -34.59375 L 81.328125 -100.515625 L 93.59375 -100.515625 L 93.59375 0 Z M 77.1875 0 "/></g></g></g></g></g></g></svg>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
<svg width="234" height="42" viewBox="0 0 234 42" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<path d="M18 30H6V18H18V30Z" fill="#4B4646"/>
|
|
3
|
+
<path d="M18 12H6V30H18V12ZM24 36H0V6H24V36Z" fill="#B7B1B1"/>
|
|
4
|
+
<path d="M48 30H36V18H48V30Z" fill="#4B4646"/>
|
|
5
|
+
<path d="M36 30H48V12H36V30ZM54 36H36V42H30V6H54V36Z" fill="#B7B1B1"/>
|
|
6
|
+
<path d="M84 24V30H66V24H84Z" fill="#4B4646"/>
|
|
7
|
+
<path d="M84 24H66V30H84V36H60V6H84V24ZM66 18H78V12H66V18Z" fill="#B7B1B1"/>
|
|
8
|
+
<path d="M108 36H96V18H108V36Z" fill="#4B4646"/>
|
|
9
|
+
<path d="M108 12H96V36H90V6H108V12ZM114 36H108V12H114V36Z" fill="#B7B1B1"/>
|
|
10
|
+
<path d="M144 30H126V18H144V30Z" fill="#4B4646"/>
|
|
11
|
+
<path d="M144 12H126V30H144V36H120V6H144V12Z" fill="#F1ECEC"/>
|
|
12
|
+
<path d="M168 30H156V18H168V30Z" fill="#4B4646"/>
|
|
13
|
+
<path d="M168 12H156V30H168V12ZM174 36H150V6H174V36Z" fill="#F1ECEC"/>
|
|
14
|
+
<path d="M198 30H186V18H198V30Z" fill="#4B4646"/>
|
|
15
|
+
<path d="M198 12H186V30H198V12ZM204 36H180V6H198V0H204V36Z" fill="#F1ECEC"/>
|
|
16
|
+
<path d="M234 24V30H216V24H234Z" fill="#4B4646"/>
|
|
17
|
+
<path d="M216 12V18H228V12H216ZM234 24H216V30H234V36H210V6H234V24Z" fill="#F1ECEC"/>
|
|
18
|
+
</svg>
|