<?php
// artifacts.php - Artifact Management System
session_start();
// Initialize session artifacts if not exists
if (!isset($_SESSION['artifacts'])) {
$_SESSION['artifacts'] = [];
}
// Handle artifact operations
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$action = $_POST['action'] ?? '';
switch ($action) {
case 'save_artifact':
$id = trim($_POST['artifact_id'] ?? '');
$title = trim($_POST['artifact_title'] ?? '');
$content = $_POST['artifact_content'] ?? '';
if ($id && $title) {
$_SESSION['artifacts'][$id] = [
'title' => $title,
'content' => $content,
'created' => $_SESSION['artifacts'][$id]['created'] ?? time(),
'modified' => time()
];
}
break;
case 'delete_artifact':
$id = $_POST['artifact_id'] ?? '';
if ($id && isset($_SESSION['artifacts'][$id])) {
unset($_SESSION['artifacts'][$id]);
}
break;
case 'create_new':
$newId = 'artifact_' . time() . '_' . rand(1000, 9999);
$_SESSION['artifacts'][$newId] = [
'title' => 'New Artifact',
'content' => '',
'created' => time(),
'modified' => time()
];
break;
}
// Redirect to prevent form resubmission
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
function h($s) { return htmlspecialchars($s ?? '', ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); }
?>
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>Artifacts Manager</title>
<style>
:root{
--bg:#0f1115; --panel:#151823; --ink:#e8e8e8; --muted:#9aa0a6; --br:#252a36;
--accent:#2f6feb; --bot:#2a2f3a; --sep:#2b3242; --code:#0b0e14;
--gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
--gradient-secondary: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
--glow-color: #4f46e5;
--danger: #ef4444;
--success: #10b981;
}
* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }
body {
background: var(--bg);
color: var(--ink);
font-family: system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
display: flex;
flex-direction: column;
}
/* Header */
.header {
height: 56px;
background: var(--panel);
border-bottom: 1px solid var(--br);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 16px;
}
.title { font-weight: 600; font-size: 1.1rem; }
.header-actions {
display: flex;
gap: 8px;
}
.btn {
background: #1b1f2a;
color: var(--ink);
border: 1px solid var(--br);
border-radius: 10px;
padding: 8px 16px;
cursor: pointer;
font-size: 0.9rem;
text-decoration: none;
display: inline-flex;
align-items: center;
gap: 6px;
transition: all 0.2s ease;
}
.btn:hover { border-color: var(--accent); }
.btn-primary {
background: var(--accent);
border-color: var(--accent);
color: white;
}
.btn-primary:hover {
filter: brightness(1.1);
}
.btn-danger {
background: var(--danger);
border-color: var(--danger);
color: white;
}
.btn-fullscreen {
background: var(--gradient-primary);
border: none;
color: white;
font-weight: 600;
}
/* Main Layout */
.main {
flex: 1;
display: flex;
overflow: hidden;
}
/* Sidebar */
.sidebar {
width: 280px;
background: var(--panel);
border-right: 1px solid var(--br);
display: flex;
flex-direction: column;
}
.sidebar-header {
padding: 16px;
border-bottom: 1px solid var(--br);
}
.artifact-list {
flex: 1;
overflow-y: auto;
padding: 8px;
}
.artifact-item {
display: flex;
align-items: center;
gap: 12px;
padding: 12px;
margin-bottom: 4px;
border-radius: 8px;
cursor: pointer;
transition: all 0.2s ease;
border: 1px solid transparent;
}
.artifact-item:hover {
background: rgba(255, 255, 255, 0.05);
border-color: var(--br);
}
.artifact-item.active {
background: rgba(47, 111, 235, 0.1);
border-color: var(--accent);
}
.artifact-radio {
width: 16px;
height: 16px;
accent-color: var(--accent);
}
.artifact-info {
flex: 1;
min-width: 0;
}
.artifact-title {
font-weight: 500;
font-size: 0.9rem;
margin-bottom: 2px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.artifact-meta {
font-size: 0.75rem;
color: var(--muted);
}
.artifact-delete {
opacity: 0;
background: var(--danger);
border: none;
color: white;
width: 24px;
height: 24px;
border-radius: 4px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
transition: opacity 0.2s ease;
}
.artifact-item:hover .artifact-delete {
opacity: 1;
}
/* Content Area */
.content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
}
.content-header {
padding: 16px;
border-bottom: 1px solid var(--br);
background: var(--panel);
}
.content-body {
flex: 1;
padding: 16px;
display: flex;
flex-direction: column;
gap: 16px;
overflow: auto;
}
.form-group {
display: flex;
flex-direction: column;
gap: 6px;
}
.form-label {
font-size: 0.9rem;
font-weight: 500;
color: var(--ink);
}
.form-input {
background: #0d111a;
color: var(--ink);
border: 1px solid var(--br);
border-radius: 8px;
padding: 10px 12px;
font-size: 0.95rem;
font-family: inherit;
}
.form-input:focus {
outline: none;
border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(47, 111, 235, 0.1);
}
.form-textarea {
resize: vertical;
min-height: 400px;
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
font-size: 0.9rem;
line-height: 1.5;
}
.form-actions {
display: flex;
gap: 12px;
padding-top: 8px;
}
/* Empty State */
.empty-state {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
color: var(--muted);
gap: 16px;
}
.empty-icon {
width: 64px;
height: 64px;
opacity: 0.5;
}
/* Responsive */
@media (max-width: 768px) {
.sidebar {
width: 100%;
max-height: 200px;
}
.main {
flex-direction: column;
}
.artifact-item {
padding: 8px;
}
.content-body {
padding: 12px;
}
}
</style>
</head>
<body>
<div class="header">
<div class="title">Artifacts Manager</div>
<div class="header-actions">
<a href="index.php" class="btn">← Back to Chat</a>
<button class="btn btn-primary" onclick="submitToChat()" id="submitBtn" disabled>
📤 Submit to Chat
</button>
</div>
</div>
<div class="main">
<!-- Sidebar -->
<div class="sidebar">
<div class="sidebar-header">
<form method="post" style="margin: 0;">
<button type="submit" name="action" value="create_new" class="btn btn-primary" style="width: 100%;">
+ New Artifact
</button>
</form>
</div>
<div class="artifact-list">
<?php if (empty($_SESSION['artifacts'])): ?>
<div style="padding: 20px; text-align: center; color: var(--muted); font-size: 0.9rem;">
No artifacts yet.<br>Create your first one!
</div>
<?php else: ?>
<?php foreach ($_SESSION['artifacts'] as $id => $artifact): ?>
<div class="artifact-item" onclick="selectArtifact('<?= h($id) ?>')" id="item-<?= h($id) ?>">
<input type="radio" name="selected_artifact" value="<?= h($id) ?>" class="artifact-radio" id="radio-<?= h($id) ?>">
<div class="artifact-info">
<div class="artifact-title"><?= h($artifact['title']) ?></div>
<div class="artifact-meta">
Modified <?= date('M j, Y g:i A', $artifact['modified']) ?>
</div>
</div>
<form method="post" style="margin: 0;" onclick="event.stopPropagation();">
<input type="hidden" name="action" value="delete_artifact">
<input type="hidden" name="artifact_id" value="<?= h($id) ?>">
<button type="submit" class="artifact-delete" onclick="return confirm('Delete this artifact?')" title="Delete">×</button>
</form>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
<!-- Content Area -->
<div class="content">
<div class="content-header">
<h2 id="content-title">Select an artifact to edit</h2>
</div>
<div class="content-body">
<div id="editor-form" style="display: none;">
<form method="post" id="artifact-form">
<input type="hidden" name="action" value="save_artifact">
<input type="hidden" name="artifact_id" id="current-artifact-id">
<div class="form-group">
<label class="form-label" for="artifact-title">Title</label>
<input type="text" id="artifact-title" name="artifact_title" class="form-input" placeholder="Enter artifact title...">
</div>
<div class="form-group">
<label class="form-label" for="artifact-content">Content</label>
<textarea id="artifact-content" name="artifact_content" class="form-input form-textarea" placeholder="Enter your artifact content here..."></textarea>
</div>
<div class="form-actions">
<button type="submit" class="btn btn-primary">💾 Save Artifact</button>
<button type="button" class="btn" onclick="clearEditor()">Clear</button>
</div>
</form>
</div>
<div id="empty-state" class="empty-state">
<svg class="empty-icon" fill="currentColor" viewBox="0 0 24 24">
<path d="M14,2H6A2,2 0 0,0 4,4V20A2,2 0 0,0 6,22H18A2,2 0 0,0 20,20V8L14,2M18,20H6V4H13V9H18V20Z" />
</svg>
<h3>No artifact selected</h3>
<p>Choose an artifact from the sidebar to start editing, or create a new one.</p>
</div>
</div>
</div>
</div>
<script>
// Artifact data from PHP
const artifacts = <?= json_encode($_SESSION['artifacts']) ?>;
let currentArtifactId = null;
// Select artifact function
function selectArtifact(artifactId) {
// Update radio button
document.getElementById('radio-' + artifactId).checked = true;
// Update visual selection
document.querySelectorAll('.artifact-item').forEach(item => {
item.classList.remove('active');
});
document.getElementById('item-' + artifactId).classList.add('active');
// Load artifact data
const artifact = artifacts[artifactId];
if (artifact) {
currentArtifactId = artifactId;
document.getElementById('current-artifact-id').value = artifactId;
document.getElementById('artifact-title').value = artifact.title;
document.getElementById('artifact-content').value = artifact.content;
document.getElementById('content-title').textContent = artifact.title;
document.getElementById('editor-form').style.display = 'block';
document.getElementById('empty-state').style.display = 'none';
// Enable submit button
document.getElementById('submitBtn').disabled = false;
}
}
// Clear editor
function clearEditor() {
document.getElementById('artifact-title').value = '';
document.getElementById('artifact-content').value = '';
}
// Auto-save on content change (debounced)
let saveTimeout;
function autoSave() {
clearTimeout(saveTimeout);
saveTimeout = setTimeout(() => {
if (currentArtifactId) {
const form = document.getElementById('artifact-form');
const formData = new FormData(form);
fetch(window.location.href, {
method: 'POST',
body: formData
}).then(() => {
// Update local artifacts object
artifacts[currentArtifactId].title = document.getElementById('artifact-title').value;
artifacts[currentArtifactId].content = document.getElementById('artifact-content').value;
artifacts[currentArtifactId].modified = Date.now() / 1000;
// Update sidebar display
const titleElement = document.querySelector(`#item-${currentArtifactId} .artifact-title`);
if (titleElement) {
titleElement.textContent = artifacts[currentArtifactId].title;
}
});
}
}, 2000);
}
// Add auto-save listeners
document.getElementById('artifact-title').addEventListener('input', autoSave);
document.getElementById('artifact-content').addEventListener('input', autoSave);
// Submit to chat functionality
function submitToChat() {
if (currentArtifactId && artifacts[currentArtifactId]) {
const title = artifacts[currentArtifactId].title;
const content = artifacts[currentArtifactId].content;
// Create a compact version for chat submission
const compactContent = content.substring(0, 200) + (content.length > 200 ? '...' : '');
// Store in session for chat to pick up
const chatData = {
title: title,
content: content,
compactContent: compactContent,
timestamp: Date.now()
};
// Send to backend to store in session
fetch('submit_artifact.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(chatData)
}).then(response => response.json())
.then(data => {
if (data.success) {
// Add delay to ensure session write completes
setTimeout(() => {
window.location.href = 'index.php?artifact_submitted=1&_=' + Date.now();
}, 500); // 500ms delay
} else {
alert('Error submitting artifact to chat');
}
}).catch(error => {
console.error('Error:', error);
alert('Error submitting artifact to chat');
});
}
}
// Prevent form submission on Enter in title field
document.getElementById('artifact-title').addEventListener('keydown', function(e) {
if (e.key === 'Enter') {
e.preventDefault();
document.getElementById('artifact-content').focus();
}
});
// Initialize first artifact if available
window.addEventListener('load', function() {
const firstArtifact = Object.keys(artifacts)[0];
if (firstArtifact) {
selectArtifact(firstArtifact);
}
});
</script>
</body>
</html>