Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,19 @@

This guide walks you through deploying an OpenTelemetry collector against an existing Managed ClickStack service, or adapting your existing collector, before verifying that data is flowing through.

<AgentPrompt
prompt="Use curl to download, read and follow: https://clickhouse.com/docs/skills/clickstack-otel-collector/SKILL.md"
description="Your agent will set up the ClickStack OpenTelemetry collector for you. Works with Claude Code, Cursor, Codex, and other coding agents."
outline={[
"Install clickhousectl if missing and authenticate it (asks you for a ClickHouse Cloud API key — paste in chat or run the login command yourself).",

Check warning on line 26 in docs/use-cases/observability/clickstack/managed-onboarding/setting-up-your-opentelemetry-collector.md

View workflow job for this annotation

GitHub Actions / vale

ClickHouse.Quotes

Commas and periods go inside quotation marks.
"Ask you for the target ClickHouse Cloud service ID or name, then fetch its HTTPS endpoint.",

Check warning on line 27 in docs/use-cases/observability/clickstack/managed-onboarding/setting-up-your-opentelemetry-collector.md

View workflow job for this annotation

GitHub Actions / vale

ClickHouse.Quotes

Commas and periods go inside quotation marks.
"Create a hyperdx_ingest SQL user on the service (generates a strong password, or uses one you provide).",

Check warning on line 28 in docs/use-cases/observability/clickstack/managed-onboarding/setting-up-your-opentelemetry-collector.md

View workflow job for this annotation

GitHub Actions / vale

ClickHouse.Quotes

Commas and periods go inside quotation marks.
"Run the ClickStack OpenTelemetry collector locally in Docker, pointed at your service.",

Check warning on line 29 in docs/use-cases/observability/clickstack/managed-onboarding/setting-up-your-opentelemetry-collector.md

View workflow job for this annotation

GitHub Actions / vale

ClickHouse.Quotes

Commas and periods go inside quotation marks.
"Send a short burst of synthetic logs, traces, and metrics through the collector to prove the pipeline works.",

Check warning on line 30 in docs/use-cases/observability/clickstack/managed-onboarding/setting-up-your-opentelemetry-collector.md

View workflow job for this annotation

GitHub Actions / vale

ClickHouse.Quotes

Commas and periods go inside quotation marks.
"Verify the data has landed in the otel database, and hand you the ClickStack UI URL where you can view it."
]}
/>

The collector runs as a **gateway**: a single OTLP endpoint that your applications, SDKs, and agent collectors send to. The gateway batches events, applies any processing you've configured, and writes them to ClickHouse via the [ClickHouse exporter](https://github.com/open-telemetry/opentelemetry-collector-contrib/tree/main/exporter/clickhouseexporter). This pattern keeps collection logic out of your application code and lets you scale ingestion independently of the workloads producing data. For background on gateway versus agent roles, see [Collector roles](/use-cases/observability/clickstack/ingesting-data/otel-collector#collector-roles).

:::note Existing collector
Expand Down
131 changes: 131 additions & 0 deletions src/components/AgentPrompt/AgentPrompt.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import React, { useCallback, useState } from 'react';
import styles from './styles.module.scss';

interface AgentPromptProps {
prompt: string;
title?: string;
description?: React.ReactNode;
outline?: string[];
outlineLabel?: string;
}

const CopyIcon = () => (
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<rect x="9" y="9" width="13" height="13" rx="2" ry="2" />
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" />
</svg>
);

const CheckIcon = () => (
<svg
width="14"
height="14"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
strokeLinejoin="round"
aria-hidden="true"
>
<polyline points="20 6 9 17 4 12" />
</svg>
);

const AgentPrompt: React.FC<AgentPromptProps> = ({
prompt,
title = 'Agent-Assisted Setup',
description,
outline,
outlineLabel = 'What the agent will do',
}) => {
const [copied, setCopied] = useState(false);

const handleCopy = useCallback(async () => {
try {
if (navigator?.clipboard?.writeText) {
await navigator.clipboard.writeText(prompt);
} else {
const ta = document.createElement('textarea');
ta.value = prompt;
ta.style.position = 'fixed';
ta.style.opacity = '0';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
}
setCopied(true);
window.setTimeout(() => setCopied(false), 2000);
} catch {
/* swallow — fallback already attempted */
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clipboard failure skips textarea fallback

Low Severity

handleCopy only uses the textarea/execCommand path when the Clipboard API is absent. If navigator.clipboard.writeText exists but throws (permissions, policy, or transient errors), the catch block exits with no fallback and the UI never shows success—so the copy widget can fail silently on the primary action.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 889943e. Configure here.

}, [prompt]);

return (
<div className={styles.wrapper} data-mdast="ignore">
<div className={styles.mainRow}>
<div className={styles.left}>
<span className={styles.title}>{title}</span>
</div>
<div className={styles.promptArea}>
<code className={styles.promptText}>{prompt}</code>
</div>
<button
type="button"
className={styles.copyButton}
onClick={handleCopy}
aria-label={copied ? 'Copied' : 'Copy prompt'}
>
{copied ? <CheckIcon /> : <CopyIcon />}
<span>{copied ? 'Copied' : 'Copy Prompt'}</span>
</button>
</div>
{description && (
<div className={styles.subRow}>
<span className={styles.description}>{description}</span>
</div>
)}
{outline && outline.length > 0 && (
<details className={styles.outline}>
<summary className={styles.outlineSummary}>
<svg
width="12"
height="12"
viewBox="0 0 15 15"
fill="none"
xmlns="http://www.w3.org/2000/svg"
className={styles.outlineChevron}
aria-hidden="true"
>
<path
d="M6.1584 3.13508C6.35985 2.94621 6.67627 2.95642 6.86514 3.15788L10.6151 7.15788C10.7954 7.3502 10.7954 7.64949 10.6151 7.84182L6.86514 11.8418C6.67627 12.0433 6.35985 12.0535 6.1584 11.8646C5.95694 11.6757 5.94673 11.3593 6.1356 11.1579L9.565 7.49985L6.1356 3.84182C5.94673 3.64036 5.95694 3.32394 6.1584 3.13508Z"
fill="currentColor"
fillRule="evenodd"
clipRule="evenodd"
/>
</svg>
<span>{outlineLabel}</span>
</summary>
<ol className={styles.outlineList}>
{outline.map((item, i) => (
<li key={i}>{item}</li>
))}
</ol>
</details>
)}
</div>
);
};

export default AgentPrompt;
1 change: 1 addition & 0 deletions src/components/AgentPrompt/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './AgentPrompt';
195 changes: 195 additions & 0 deletions src/components/AgentPrompt/styles.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,195 @@
// AgentPrompt — ClickStack yellow agent-prompt callout.
// Mirrors the principle of Sentry's "Agent-Assisted Setup" widget but
// rebranded to ClickStack's brand yellow (--palette_brand_350 / 300 / 50).

.wrapper {
--agent-accent: var(--palette_brand_350); // #FBFF46
--agent-accent-soft: var(--palette_brand_300); // #FCFF74
--agent-accent-bg: var(--palette_brand_50); // #FFFFE8
--agent-accent-bg-strong: var(--palette_brand_100); // #FEFFBA
--agent-text: var(--palette_slate_900);
--agent-text-muted: var(--palette_slate_600);
--agent-prompt-bg: #FFFFFF;
--agent-prompt-border: var(--palette_brand_300);
--agent-button-bg: var(--palette_brand_350);
--agent-button-bg-hover: var(--palette_brand_400);
--agent-button-text: var(--palette_slate_900);

border: 1px solid var(--agent-accent);
background: var(--agent-accent-bg);
border-radius: 8px;
padding: 14px 16px;
margin: 1.25rem 0 1.75rem;
display: flex;
flex-direction: column;
gap: 10px;
box-sizing: border-box;
}

.mainRow {
display: flex;
align-items: center;
gap: 12px;
flex-wrap: nowrap;
}

.left {
flex-shrink: 0;
}

.title {
font-weight: 600;
font-size: 0.875rem;
color: var(--agent-text);
white-space: nowrap;
}

.promptArea {
flex: 1 1 auto;
min-width: 0;
background: var(--agent-prompt-bg);
border: 1px solid var(--agent-prompt-border);
border-radius: 6px;
padding: 6px 10px;
overflow-x: auto;

// Hide scrollbar but keep scrollability
scrollbar-width: thin;
&::-webkit-scrollbar { height: 6px; }
&::-webkit-scrollbar-thumb {
background: var(--palette_brand_300);
border-radius: 3px;
}
}

.promptText {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
font-size: 0.8125rem;
color: var(--agent-text);
background: transparent;
border: none;
padding: 0;
white-space: nowrap;
display: block;
}

.copyButton {
flex-shrink: 0;
display: inline-flex;
align-items: center;
gap: 6px;
background: var(--agent-button-bg);
color: var(--agent-button-text);
border: 1px solid var(--agent-accent);
border-radius: 6px;
padding: 7px 12px;
font-size: 0.8125rem;
font-weight: 600;
cursor: pointer;
transition: background-color 120ms ease, transform 80ms ease;

&:hover {
background: var(--agent-button-bg-hover);
}
&:active {
transform: translateY(1px);
}
&:focus-visible {
outline: 2px solid var(--palette_info_400);
outline-offset: 2px;
}
}

.subRow {
padding-left: 2px;
}

.description {
font-size: 0.8125rem;
color: var(--agent-text-muted);
line-height: 1.4;
}

.outline {
border-top: 1px dashed var(--agent-prompt-border);
padding-top: 8px;
margin-top: 2px;
}

.outlineSummary {
display: inline-flex;
align-items: center;
gap: 6px;
cursor: pointer;
font-size: 0.8125rem;
font-weight: 600;
color: var(--agent-text);
list-style: none;
user-select: none;
padding: 2px 0;

&::-webkit-details-marker { display: none; }
&:hover { opacity: 0.85; }
&:focus-visible {
outline: 2px solid var(--palette_info_400);
outline-offset: 2px;
border-radius: 3px;
}
}

.outlineChevron {
transition: transform 150ms ease;
flex-shrink: 0;
}

details[open] > .outlineSummary > .outlineChevron {
transform: rotate(90deg);
}

.outlineList {
margin: 8px 0 2px 0;
padding-left: 1.4rem;
font-size: 0.8125rem;
color: var(--agent-text-muted);
line-height: 1.5;

li {
margin: 2px 0;
padding-left: 2px;
}
li::marker {
color: var(--agent-text);
font-weight: 600;
}
}

@media (max-width: 768px) {
.mainRow {
flex-wrap: wrap;
}
.promptArea {
order: 3;
width: 100%;
flex-basis: 100%;
}
.copyButton {
margin-left: auto;
}
.promptText {
white-space: pre-wrap;
word-break: break-all;
}
}

[data-theme='dark'] .wrapper {
--agent-accent: var(--palette_brand_300);
--agent-accent-bg: rgba(251, 255, 70, 0.06);
--agent-text: var(--palette_neutral_0);
--agent-text-muted: var(--palette_neutral_200);
--agent-prompt-bg: var(--palette_neutral_800);
--agent-prompt-border: rgba(251, 255, 70, 0.35);
--agent-button-bg: var(--palette_brand_300);
--agent-button-bg-hover: var(--palette_brand_350);
--agent-button-text: var(--palette_neutral_900);
}

2 changes: 2 additions & 0 deletions src/theme/MDXComponents.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import MDXComponents from '@theme-original/MDXComponents';
import VStepper from '@site/src/components/Stepper/Stepper';
import GlossaryTooltip from '@site/src/components/GlossaryTooltip/GlossaryTooltip';
import KapaLink from '@site/src/components/KapaAI/KapaLink';
import AgentPrompt from '@site/src/components/AgentPrompt';

// Define the enhanced components
const enhancedComponents = {
...MDXComponents,
AgentPrompt,
KapaLink,
GlossaryTooltip,
ul: (props) => <ul className="custom-ul" {...props} />,
Expand Down
Loading
Loading