<?php
// artifacts.php - Artifacts Management
// Initialize artifacts session
if (!isset($_SESSION['artifacts'])) $_SESSION['artifacts'] = [];
// Handle artifact form submission
if (isset($_POST['action']) && $_POST['action'] === 'save_artifacts') {
$_SESSION['artifacts'] = [];
// Process each artifact
if (isset($_POST['artifacts']) && is_array($_POST['artifacts'])) {
foreach ($_POST['artifacts'] as $artifact) {
if (!empty(trim($artifact['title'])) && !empty(trim($artifact['content']))) {
$_SESSION['artifacts'][] = [
'title' => trim($artifact['title']),
'type' => $artifact['type'] ?? 'code',
'language' => $artifact['language'] ?? 'javascript',
'content' => trim($artifact['content'])
];
}
}
}
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
// Handle artifact deletion
if (isset($_POST['action']) && $_POST['action'] === 'clear_artifacts') {
$_SESSION['artifacts'] = [];
header('Location: ' . $_SERVER['PHP_SELF']);
exit;
}
$artifacts = $_SESSION['artifacts'];
?>
<style>
/* Artifacts-specific styles */
.artifacts-tabs {
display: flex;
gap: 4px;
margin-bottom: 16px;
border-bottom: 1px solid var(--br);
overflow-x: auto;
}
.artifact-tab {
background: none;
border: none;
color: var(--muted);
padding: 8px 12px;
cursor: pointer;
border-bottom: 2px solid transparent;
white-space: nowrap;
font-size: 0.85rem;
transition: all 0.2s ease;
}
.artifact-tab.active {
color: var(--accent);
border-bottom-color: var(--accent);
}
.artifact-tab:hover {
color: var(--ink);
}
.artifact-panel {
display: none;
height: 60vh;
overflow-y: auto;
}
.artifact-panel.active {
display: block;
}
.artifact-content-editor {
display: grid;
grid-template-rows: auto 1fr;
height: 100%;
gap: 12px;
}
.artifact-meta {
display: grid;
grid-template-columns: 1fr auto auto auto;
gap: 8px;
align-items: center;
}
.artifact-content {
width: 100%;
min-height: calc(60vh - 80px);
font-family: 'Courier New', monospace;
font-size: 0.9rem;
line-height: 1.4;
resize: none;
border: 1px solid var(--br);
border-radius: 8px;
padding: 12px;
background: var(--code);
}
.artifact-remove {
background: #dc2626;
color: white;
border: none;
border-radius: 4px;
padding: 4px 8px;
cursor: pointer;
font-size: 0.75rem;
min-width: 60px;
}
.artifact-remove:hover {
background: #b91c1c;
}
.add-artifact {
background: var(--accent);
color: white;
border: none;
border-radius: 8px;
padding: 10px 16px;
cursor: pointer;
margin-bottom: 16px;
font-size: 0.9rem;
}
.add-artifact:hover {
filter: brightness(1.1);
}
.artifacts-actions {
display: flex;
gap: 12px;
margin-top: 16px;
}
.clear-artifacts {
background: #dc2626;
color: white;
}
.clear-artifacts:hover {
background: #b91c1c;
}
.artifact-count {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 0.8rem;
color: var(--accent);
margin-left: 8px;
}
.artifact-count::before {
content: '📄';
font-size: 12px;
}
@media (max-width: 640px) {
.artifact-meta {
grid-template-columns: 1fr;
gap: 8px;
}
.artifact-meta > * {
width: 100%;
}
}
</style>
<div class="overlay" id="artifacts">
<div class="dialog">
<div class="dialog-hd">
<strong>Initial Artifacts</strong>
<button class="close-x" onclick="hideArtifacts()">×</button>
</div>
<div class="dialog-bd">
<p class="small-note">Add code, HTML, or other content that will be available at the start of conversations. These artifacts will be included in the initial context.</p>
<form method="post" id="artifactsForm">
<input type="hidden" name="action" value="save_artifacts">
<button type="button" class="add-artifact" onclick="addArtifact()">+ Add Artifact</button>
<div class="artifacts-tabs" id="artifactTabs">
<?php if (empty($artifacts)): ?>
<button type="button" class="artifact-tab active" onclick="switchTab(0)">New Artifact</button>
<?php else: ?>
<?php foreach ($artifacts as $index => $artifact): ?>
<button type="button" class="artifact-tab <?= $index === 0 ? 'active' : '' ?>" onclick="switchTab(<?= $index ?>)">
<?= h($artifact['title'] ?: 'Artifact ' . ($index + 1)) ?>
</button>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div id="artifacts-container">
<?php if (empty($artifacts)): ?>
<!-- Default empty artifact -->
<div class="artifact-panel active" id="artifact-0">
<div class="artifact-content-editor">
<div class="artifact-meta">
<input type="text" name="artifacts[0][title]" placeholder="Artifact title" value="" onchange="updateTabTitle(0, this.value)">
<select name="artifacts[0][type]">
<option value="code">Code</option>
<option value="html">HTML</option>
<option value="markdown">Markdown</option>
<option value="text">Text</option>
</select>
<select name="artifacts[0][language]">
<option value="javascript">JavaScript</option>
<option value="python">Python</option>
<option value="php">PHP</option>
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="sql">SQL</option>
<option value="json">JSON</option>
<option value="xml">XML</option>
<option value="bash">Bash</option>
<option value="other">Other</option>
</select>
<button type="button" class="artifact-remove" onclick="removeArtifact(0)">Remove</button>
</div>
<textarea name="artifacts[0][content]" class="artifact-content" placeholder="Enter your code or content here..."></textarea>
</div>
</div>
<?php else: ?>
<?php foreach ($artifacts as $index => $artifact): ?>
<div class="artifact-panel <?= $index === 0 ? 'active' : '' ?>" id="artifact-<?= $index ?>">
<div class="artifact-content-editor">
<div class="artifact-meta">
<input type="text" name="artifacts[<?= $index ?>][title]" placeholder="Artifact title" value="<?= h($artifact['title']) ?>" onchange="updateTabTitle(<?= $index ?>, this.value)">
<select name="artifacts[<?= $index ?>][type]">
<option value="code" <?= $artifact['type'] === 'code' ? 'selected' : '' ?>>Code</option>
<option value="html" <?= $artifact['type'] === 'html' ? 'selected' : '' ?>>HTML</option>
<option value="markdown" <?= $artifact['type'] === 'markdown' ? 'selected' : '' ?>>Markdown</option>
<option value="text" <?= $artifact['type'] === 'text' ? 'selected' : '' ?>>Text</option>
</select>
<select name="artifacts[<?= $index ?>][language]">
<option value="javascript" <?= $artifact['language'] === 'javascript' ? 'selected' : '' ?>>JavaScript</option>
<option value="python" <?= $artifact['language'] === 'python' ? 'selected' : '' ?>>Python</option>
<option value="php" <?= $artifact['language'] === 'php' ? 'selected' : '' ?>>PHP</option>
<option value="html" <?= $artifact['language'] === 'html' ? 'selected' : '' ?>>HTML</option>
<option value="css" <?= $artifact['language'] === 'css' ? 'selected' : '' ?>>CSS</option>
<option value="sql" <?= $artifact['language'] === 'sql' ? 'selected' : '' ?>>SQL</option>
<option value="json" <?= $artifact['language'] === 'json' ? 'selected' : '' ?>>JSON</option>
<option value="xml" <?= $artifact['language'] === 'xml' ? 'selected' : '' ?>>XML</option>
<option value="bash" <?= $artifact['language'] === 'bash' ? 'selected' : '' ?>>Bash</option>
<option value="other" <?= $artifact['language'] === 'other' ? 'selected' : '' ?>>Other</option>
</select>
<button type="button" class="artifact-remove" onclick="removeArtifact(<?= $index ?>)">Remove</button>
</div>
<textarea name="artifacts[<?= $index ?>][content]" class="artifact-content" placeholder="Enter your code or content here..."><?= h($artifact['content']) ?></textarea>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
<div class="artifacts-actions">
<button type="submit" class="btn">Save Artifacts</button>
<?php if (!empty($artifacts)): ?>
<button type="button" class="btn clear-artifacts" onclick="clearArtifacts()">Clear All</button>
<?php endif; ?>
</div>
</form>
</div>
<div class="dialog-ft">
<button class="btn" onclick="hideArtifacts()">Close</button>
</div>
</div>
</div>
<script>
// Artifacts management
function hideArtifacts() {
document.getElementById('artifacts').style.display = 'none';
}
// Close modal when clicking outside
document.getElementById('artifacts').addEventListener('click', function(e) {
if (e.target === this) hideArtifacts();
});
let artifactCounter = <?= count($artifacts) ?: 1 ?>;
let currentTab = 0;
function switchTab(index) {
// Hide all panels
document.querySelectorAll('.artifact-panel').forEach(panel => {
panel.classList.remove('active');
});
// Hide all tabs
document.querySelectorAll('.artifact-tab').forEach(tab => {
tab.classList.remove('active');
});
// Show selected panel and tab
const panel = document.getElementById(`artifact-${index}`);
const tab = document.querySelectorAll('.artifact-tab')[index];
if (panel) panel.classList.add('active');
if (tab) tab.classList.add('active');
currentTab = index;
}
function updateTabTitle(index, title) {
const tab = document.querySelectorAll('.artifact-tab')[index];
if (tab) {
tab.textContent = title || `Artifact ${index + 1}`;
}
}
function addArtifact() {
const container = document.getElementById('artifacts-container');
const tabsContainer = document.getElementById('artifactTabs');
const newIndex = artifactCounter++;
// Add new tab
const newTab = document.createElement('button');
newTab.type = 'button';
newTab.className = 'artifact-tab';
newTab.textContent = 'New Artifact';
newTab.onclick = () => switchTab(newIndex);
tabsContainer.appendChild(newTab);
// Add new panel
const artifactHtml = `
<div class="artifact-panel" id="artifact-${newIndex}">
<div class="artifact-content-editor">
<div class="artifact-meta">
<input type="text" name="artifacts[${newIndex}][title]" placeholder="Artifact title" value="" onchange="updateTabTitle(${newIndex}, this.value)">
<select name="artifacts[${newIndex}][type]">
<option value="code">Code</option>
<option value="html">HTML</option>
<option value="markdown">Markdown</option>
<option value="text">Text</option>
</select>
<select name="artifacts[${newIndex}][language]">
<option value="javascript">JavaScript</option>
<option value="python">Python</option>
<option value="php">PHP</option>
<option value="html">HTML</option>
<option value="css">CSS</option>
<option value="sql">SQL</option>
<option value="json">JSON</option>
<option value="xml">XML</option>
<option value="bash">Bash</option>
<option value="other">Other</option>
</select>
<button type="button" class="artifact-remove" onclick="removeArtifact(${newIndex})">Remove</button>
</div>
<textarea name="artifacts[${newIndex}][content]" class="artifact-content" placeholder="Enter your code or content here..."></textarea>
</div>
</div>
`;
container.insertAdjacentHTML('beforeend', artifactHtml);
// Switch to the new tab
switchTab(newIndex);
}
function removeArtifact(index) {
const panel = document.getElementById(`artifact-${index}`);
const tabs = document.querySelectorAll('.artifact-tab');
const tab = tabs[index];
if (panel) panel.remove();
if (tab) tab.remove();
// Switch to first available tab
const remainingTabs = document.querySelectorAll('.artifact-tab');
if (remainingTabs.length > 0) {
// Find the index of the first remaining tab
const firstPanel = document.querySelector('.artifact-panel');
if (firstPanel) {
const firstIndex = parseInt(firstPanel.id.split('-')[1]);
switchTab(firstIndex);
}
}
}
function clearArtifacts() {
if (confirm('Clear all artifacts? This will remove all saved artifacts.')) {
const form = document.createElement('form');
form.method = 'POST';
form.innerHTML = '<input type="hidden" name="action" value="clear_artifacts">';
document.body.appendChild(form);
form.submit();
}
}
</script>