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
5 changes: 0 additions & 5 deletions docs/partials/intro/_about-smart-label-capture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,6 @@ Smart Label Capture enables the simultaneous scanning of multiple barcodes and p

This technology is particularly beneficial in scenarios where labels contain various data points, such as serial numbers, weights, or expiry dates.

<p align="center">
<img src="/img/batch-scanning/SLC-smart-devices.jpg" width="650px" alt="Label Capture result for scanning smart device labels" />
<br/>Capturing multiple barcodes and text data from labels in a single scan
</p>

## Understanding Labels in Smart Label Capture

The first step to using Smart Label Capture is to [define the labels](../label-definitions) that you want to capture.
Expand Down
9 changes: 1 addition & 8 deletions src/components/SkillsCallout/InstallTabs.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, { useState } from 'react';

import skillsData from '@site/src/data/skills.json';
import { capturePostHogEvent } from './analytics';
import styles from './styles.module.css';

type TabKey = 'any' | 'claude-code' | 'cursor';
Expand Down Expand Up @@ -50,14 +51,6 @@ interface CommandBlockProps {
framework?: string;
}

type PostHogCapture = (event: string, props?: Record<string, unknown>) => void;

function capturePostHogEvent(event: string, props?: Record<string, unknown>): void {
if (typeof window === 'undefined') return;
const ph = (window as unknown as { posthog?: { capture?: PostHogCapture } }).posthog;
ph?.capture?.(event, props);
}

const CommandBlock: React.FC<CommandBlockProps> = ({ command, trackingId, product, framework }) => {
const [copied, setCopied] = useState(false);
const handleCopy = async () => {
Expand Down
7 changes: 7 additions & 0 deletions src/components/SkillsCallout/analytics.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
type PostHogCapture = (event: string, props?: Record<string, unknown>) => void;

export function capturePostHogEvent(event: string, props?: Record<string, unknown>): void {
if (typeof window === 'undefined') return;
const ph = (window as unknown as { posthog?: { capture?: PostHogCapture } }).posthog;
ph?.capture?.(event, props);
}
116 changes: 94 additions & 22 deletions src/components/SkillsCallout/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,13 @@ import { useLocation } from '@docusaurus/router';
import skillsData from '@site/src/data/skills.json';
import productsData from '@site/src/data/products.json';
import { parseSdksRoute } from '../utils/frameworks';
import { capturePostHogEvent } from './analytics';
import InstallTabs from './InstallTabs';
import styles from './styles.module.css';

const PRODUCT_DISAMBIGUATION_HEADING =
'Not sure which Scandit product fits your use case?';

interface SkillsCalloutProps {
product?: string;
framework?: string;
Expand Down Expand Up @@ -68,32 +72,93 @@ function getSharedMoreInfoUrl(search: string): string {
return `/sdks/${path}/agent-skills`;
}

interface CalloutDetailsProps {
heading: string;
banner?: boolean;
trackingProps: Record<string, unknown>;
children: React.ReactNode;
}

const CalloutDetails: React.FC<CalloutDetailsProps> = ({
heading,
banner = false,
trackingProps,
children,
}) => {
const className = banner ? `${styles.callout} ${styles.banner}` : styles.callout;
const handleToggle: React.ReactEventHandler<HTMLDetailsElement> = (e) => {
if (!e.currentTarget.open) return;
capturePostHogEvent('skills_callout_expanded', trackingProps);
};
// Cursor-follow spotlight: write the mouse position into CSS variables on
// the element so the radial-gradient ::before can read them.
const handleMouseMove: React.MouseEventHandler<HTMLDetailsElement> = (e) => {
const rect = e.currentTarget.getBoundingClientRect();
e.currentTarget.style.setProperty('--callout-mx', `${e.clientX - rect.left}px`);
e.currentTarget.style.setProperty('--callout-my', `${e.clientY - rect.top}px`);
};
return (
<details className={className} onToggle={handleToggle} onMouseMove={handleMouseMove}>
<summary
className={`${styles.title} ${styles.calloutSummary}`}
aria-label="Install Scandit Agent Skills"
>
<span className={styles.calloutHeading}>{heading}</span>
<span className={styles.calloutHint} aria-hidden="true">
<span className={styles.calloutHintText} />
<span className={styles.calloutChevron}>›</span>
</span>
</summary>
<div className={styles.calloutBody}>{children}</div>
</details>
);
};

interface SharedBodyProps {
sharedFrameworkSlug: string;
sharedMoreInfoUrl: string;
}

const SharedBody: React.FC<SharedBodyProps> = ({
sharedFrameworkSlug,
sharedMoreInfoUrl,
}) => (
<>
<p className={styles.description}>
Install our <code>{skillsData.shared}</code> skill so your coding
agent can answer questions about Scandit products and recommend
the right one for your use case, directly from your editor.{' '}
<a href={sharedMoreInfoUrl}>More info →</a>
</p>
<InstallTabs
skillSlug={skillsData.shared}
product="shared"
framework={sharedFrameworkSlug}
/>
</>
);

const SkillsCallout: React.FC<SkillsCalloutProps> = ({ product, framework, variant = 'product', banner = false }) => {
const { pathname, search } = useLocation();

const calloutClass = banner ? `${styles.callout} ${styles.banner}` : styles.callout;
const contentClass = banner ? styles.bannerContent : undefined;

if (variant === 'shared') {
const sharedFrameworkSlug = getSharedFrameworkSlug(search);
const sharedMoreInfoUrl = getSharedMoreInfoUrl(search);
return (
<aside className={calloutClass} aria-label="Install Scandit Agent Skills">
<div className={contentClass}>
<h3 className={styles.title}>Not sure which Scandit product fits your use case?</h3>
<p className={styles.description}>
Install our <code>{skillsData.shared}</code> skill so your coding
agent can answer questions about Scandit products and recommend
the right one for your use case, directly from your editor.{' '}
<a href={sharedMoreInfoUrl}>More info →</a>
</p>
<InstallTabs
skillSlug={skillsData.shared}
product="shared"
framework={sharedFrameworkSlug}
/>
</div>
</aside>
<CalloutDetails
heading={PRODUCT_DISAMBIGUATION_HEADING}
banner={banner}
trackingProps={{
variant: 'shared',
pathname,
framework: sharedFrameworkSlug,
}}
>
<SharedBody
sharedFrameworkSlug={sharedFrameworkSlug}
sharedMoreInfoUrl={sharedMoreInfoUrl}
/>
</CalloutDetails>
);
}

Expand All @@ -117,8 +182,15 @@ const SkillsCallout: React.FC<SkillsCalloutProps> = ({ product, framework, varia
const moreInfoUrl = frameworkPath ? `/sdks/${frameworkPath}/agent-skills` : null;

return (
<aside className={styles.callout} aria-label="Install Scandit Agent Skills">
<h3 className={styles.title}>Speed up {productName} integration with Agent Skills</h3>
<CalloutDetails
heading={`Speed up ${productName} integration with Agent Skills`}
trackingProps={{
variant: 'product',
pathname,
product: resolvedProduct,
framework: frameworkSlug,
}}
>
<p className={styles.description}>
Install the official skill to help your coding agent (Claude Code,
Codex, Cursor, etc.) integrate, debug, and customize{' '}
Expand All @@ -136,7 +208,7 @@ const SkillsCallout: React.FC<SkillsCalloutProps> = ({ product, framework, varia
product={resolvedProduct}
framework={frameworkSlug}
/>
</aside>
</CalloutDetails>
);
};

Expand Down
14 changes: 14 additions & 0 deletions src/components/SkillsCallout/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Returns true when the page should NOT show the fallback
// "Not sure which Scandit product fits your use case?" disclosure.
//
// The rule is anchored to the final non-empty path segment so unrelated
// paths like /foo/migrate-tool/bar are not caught.
export function isOnFallbackDenylist(pathname: string): boolean {
const segments = pathname.split('/').filter(Boolean);
const last = segments[segments.length - 1];
if (!last) return false;
if (last === 'release-notes') return true;
if (last === 'agent-skills') return true;
if (last.startsWith('migrate-')) return true;
return false;
}
Loading
Loading