laminark 2.21.6

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.
Files changed (40) hide show
  1. package/.claude-plugin/marketplace.json +15 -0
  2. package/README.md +182 -0
  3. package/package.json +63 -0
  4. package/plugin/.claude-plugin/plugin.json +13 -0
  5. package/plugin/.mcp.json +12 -0
  6. package/plugin/dist/analysis/worker.d.ts +1 -0
  7. package/plugin/dist/analysis/worker.js +233 -0
  8. package/plugin/dist/analysis/worker.js.map +1 -0
  9. package/plugin/dist/config-t8LZeB-u.mjs +90 -0
  10. package/plugin/dist/config-t8LZeB-u.mjs.map +1 -0
  11. package/plugin/dist/hooks/handler.d.ts +284 -0
  12. package/plugin/dist/hooks/handler.d.ts.map +1 -0
  13. package/plugin/dist/hooks/handler.js +2125 -0
  14. package/plugin/dist/hooks/handler.js.map +1 -0
  15. package/plugin/dist/index.d.ts +445 -0
  16. package/plugin/dist/index.d.ts.map +1 -0
  17. package/plugin/dist/index.js +5831 -0
  18. package/plugin/dist/index.js.map +1 -0
  19. package/plugin/dist/observations-Ch0nc47i.d.mts +170 -0
  20. package/plugin/dist/observations-Ch0nc47i.d.mts.map +1 -0
  21. package/plugin/dist/tool-registry-CZ3mJ4iR.mjs +2655 -0
  22. package/plugin/dist/tool-registry-CZ3mJ4iR.mjs.map +1 -0
  23. package/plugin/hooks/hooks.json +78 -0
  24. package/plugin/scripts/README.md +47 -0
  25. package/plugin/scripts/bump-version.sh +44 -0
  26. package/plugin/scripts/ensure-deps.sh +12 -0
  27. package/plugin/scripts/install.sh +63 -0
  28. package/plugin/scripts/local-install.sh +103 -0
  29. package/plugin/scripts/setup-tmpdir.sh +65 -0
  30. package/plugin/scripts/uninstall.sh +95 -0
  31. package/plugin/scripts/update.sh +88 -0
  32. package/plugin/scripts/verify-install.sh +43 -0
  33. package/plugin/ui/activity.js +185 -0
  34. package/plugin/ui/app.js +1642 -0
  35. package/plugin/ui/graph.js +2333 -0
  36. package/plugin/ui/help.js +228 -0
  37. package/plugin/ui/index.html +492 -0
  38. package/plugin/ui/settings.js +650 -0
  39. package/plugin/ui/styles.css +2910 -0
  40. package/plugin/ui/timeline.js +652 -0
@@ -0,0 +1,63 @@
1
+ #!/bin/bash
2
+ # Bootstrap installer for Laminark plugin.
3
+ # Works around EXDEV (cross-device rename) errors on btrfs subvolumes,
4
+ # separate /tmp partitions, and other cross-device setups.
5
+ #
6
+ # Usage: curl -fsSL <raw-url> | bash
7
+ # or: ./scripts/install.sh
8
+
9
+ set -e
10
+
11
+ echo "Laminark Marketplace Installer"
12
+ echo "==============================="
13
+ echo ""
14
+
15
+ # Check if claude CLI is available
16
+ if ! command -v claude &> /dev/null; then
17
+ echo "Error: claude CLI not found"
18
+ echo "Please install Claude Code first: https://claude.com/claude-code"
19
+ exit 1
20
+ fi
21
+
22
+ # Check if already installed
23
+ if claude plugin list 2>/dev/null | grep -q "laminark"; then
24
+ CURRENT_VERSION=$(claude plugin list 2>/dev/null | grep "laminark" | awk '{print $2}' || echo "unknown")
25
+ echo "Currently installed: v$CURRENT_VERSION"
26
+ echo ""
27
+ read -p "Update/reinstall? (Y/n): " -n 1 -r
28
+ echo ""
29
+ if [[ $REPLY =~ ^[Nn]$ ]]; then
30
+ echo "Installation cancelled."
31
+ exit 0
32
+ fi
33
+ echo "Removing existing installation..."
34
+ claude plugin remove laminark 2>/dev/null || true
35
+ fi
36
+
37
+ # Set up TMPDIR to avoid EXDEV errors
38
+ CLAUDE_DIR="${CLAUDE_HOME:-$HOME/.claude}"
39
+ SAFE_TMP="$CLAUDE_DIR/tmp"
40
+
41
+ echo ""
42
+ echo "Installing Laminark from marketplace..."
43
+ mkdir -p "$SAFE_TMP"
44
+ TMPDIR="$SAFE_TMP" claude plugin install laminark
45
+ STATUS=$?
46
+ rm -rf "$SAFE_TMP"
47
+
48
+ if [ $STATUS -eq 0 ]; then
49
+ NEW_VERSION=$(claude plugin list 2>/dev/null | grep "laminark" | awk '{print $2}' || echo "unknown")
50
+ echo ""
51
+ echo "✓ Laminark installed successfully! (v$NEW_VERSION)"
52
+ echo ""
53
+ echo "Next steps:"
54
+ echo " 1. Enable the plugin: claude plugin enable laminark"
55
+ echo " 2. Start a new Claude Code session"
56
+ echo " 3. Verify with: /mcp (should show laminark tools)"
57
+ else
58
+ echo ""
59
+ echo "✗ Installation failed with exit code $STATUS"
60
+ exit $STATUS
61
+ fi
62
+
63
+ exit 0
@@ -0,0 +1,103 @@
1
+ #!/bin/bash
2
+ # Local installation wrapper for Laminark plugin.
3
+ # Works around EXDEV (cross-device rename) errors on btrfs subvolumes,
4
+ # separate /tmp partitions, and other cross-device setups.
5
+ #
6
+ # Usage: ./scripts/local-install.sh [path-to-laminark]
7
+ # Default path: current directory (.)
8
+
9
+ set -e
10
+
11
+ # Parse path argument (default to current directory)
12
+ PLUGIN_PATH="${1:-.}"
13
+
14
+ # Resolve to absolute path
15
+ if [[ "$PLUGIN_PATH" != /* ]]; then
16
+ PLUGIN_PATH="$(cd "$PLUGIN_PATH" && pwd)"
17
+ fi
18
+
19
+ echo "Laminark Local Installer"
20
+ echo "========================"
21
+ echo ""
22
+ echo "Installing from: $PLUGIN_PATH"
23
+
24
+ # Validate prerequisites
25
+ if [ ! -d "$PLUGIN_PATH/dist" ]; then
26
+ echo "Error: dist/ directory not found in $PLUGIN_PATH"
27
+ echo "Please run 'npm install && npm run build' first"
28
+ exit 1
29
+ fi
30
+
31
+ if ! command -v claude &> /dev/null; then
32
+ echo "Error: claude CLI not found"
33
+ echo "Please install Claude Code first: https://claude.com/claude-code"
34
+ exit 1
35
+ fi
36
+
37
+ # Get version from package.json
38
+ if [ -f "$PLUGIN_PATH/package.json" ]; then
39
+ NEW_VERSION=$(grep '"version"' "$PLUGIN_PATH/package.json" | head -1 | sed -E 's/.*"version": "([^"]+)".*/\1/')
40
+ echo "Version to install: v$NEW_VERSION"
41
+ else
42
+ NEW_VERSION="unknown"
43
+ fi
44
+
45
+ # Check if already installed
46
+ if claude plugin list 2>/dev/null | grep -q "laminark"; then
47
+ CURRENT_VERSION=$(claude plugin list 2>/dev/null | grep "laminark" | awk '{print $2}' || echo "unknown")
48
+ echo "Currently installed: v$CURRENT_VERSION"
49
+ echo ""
50
+
51
+ if [ "$CURRENT_VERSION" = "$NEW_VERSION" ]; then
52
+ echo "Same version is already installed."
53
+ read -p "Reinstall anyway? (y/N): " -n 1 -r
54
+ echo ""
55
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
56
+ echo "Installation cancelled."
57
+ exit 0
58
+ fi
59
+ else
60
+ echo "An existing version is installed."
61
+ read -p "Update from v$CURRENT_VERSION to v$NEW_VERSION? (Y/n): " -n 1 -r
62
+ echo ""
63
+ if [[ $REPLY =~ ^[Nn]$ ]]; then
64
+ echo "Installation cancelled."
65
+ exit 0
66
+ fi
67
+ fi
68
+
69
+ echo "Removing existing installation..."
70
+ TMPDIR="$SAFE_TMP" claude plugin remove laminark 2>/dev/null || true
71
+ fi
72
+
73
+ # Set up safe temp directory on same filesystem as ~/.claude/
74
+ CLAUDE_DIR="${CLAUDE_HOME:-$HOME/.claude}"
75
+ SAFE_TMP="$CLAUDE_DIR/tmp"
76
+
77
+ echo ""
78
+ echo "Creating temporary directory: $SAFE_TMP"
79
+ mkdir -p "$SAFE_TMP"
80
+
81
+ # Run claude plugin add with TMPDIR override
82
+ echo "Running: claude plugin add $PLUGIN_PATH"
83
+ TMPDIR="$SAFE_TMP" claude plugin add "$PLUGIN_PATH"
84
+ STATUS=$?
85
+
86
+ # Clean up
87
+ rm -rf "$SAFE_TMP"
88
+
89
+ if [ $STATUS -eq 0 ]; then
90
+ echo ""
91
+ echo "✓ Laminark plugin installed successfully!"
92
+ echo ""
93
+ echo "Next steps:"
94
+ echo " 1. Enable the plugin: claude plugin enable laminark"
95
+ echo " 2. Start a new Claude Code session"
96
+ echo " 3. Verify with: /mcp (should show laminark tools)"
97
+ else
98
+ echo ""
99
+ echo "✗ Installation failed with exit code $STATUS"
100
+ exit $STATUS
101
+ fi
102
+
103
+ exit 0
@@ -0,0 +1,65 @@
1
+ #!/bin/bash
2
+ # Set up TMPDIR globally to avoid EXDEV errors in Claude Code
3
+ # This allows the Claude UI plugin installation to work correctly
4
+
5
+ set -e
6
+
7
+ echo "Claude Code TMPDIR Setup"
8
+ echo "========================"
9
+ echo ""
10
+ echo "This script will configure your shell to use ~/.claude/tmp as TMPDIR,"
11
+ echo "which prevents EXDEV errors when installing plugins via Claude's UI."
12
+ echo ""
13
+
14
+ # Create the temp directory
15
+ CLAUDE_DIR="${CLAUDE_HOME:-$HOME/.claude}"
16
+ TMPDIR_PATH="$CLAUDE_DIR/tmp"
17
+
18
+ mkdir -p "$TMPDIR_PATH"
19
+ echo "✓ Created directory: $TMPDIR_PATH"
20
+
21
+ # Detect shell
22
+ SHELL_NAME=$(basename "$SHELL")
23
+ if [ "$SHELL_NAME" = "zsh" ]; then
24
+ PROFILE="$HOME/.zshrc"
25
+ elif [ "$SHELL_NAME" = "bash" ]; then
26
+ PROFILE="$HOME/.bashrc"
27
+ else
28
+ echo "Warning: Unsupported shell: $SHELL_NAME"
29
+ echo "Please manually add this to your shell profile:"
30
+ echo ""
31
+ echo " export TMPDIR=\$HOME/.claude/tmp"
32
+ echo " mkdir -p \$TMPDIR"
33
+ echo ""
34
+ exit 1
35
+ fi
36
+
37
+ # Check if already configured
38
+ if grep -q "export TMPDIR.*\.claude/tmp" "$PROFILE" 2>/dev/null; then
39
+ echo ""
40
+ echo "✓ TMPDIR already configured in $PROFILE"
41
+ else
42
+ echo ""
43
+ echo "Adding TMPDIR configuration to $PROFILE..."
44
+ cat >> "$PROFILE" << 'EOF'
45
+
46
+ # Claude Code TMPDIR workaround for EXDEV errors
47
+ export TMPDIR=$HOME/.claude/tmp
48
+ mkdir -p $TMPDIR
49
+ EOF
50
+ echo "✓ Added TMPDIR configuration to $PROFILE"
51
+ fi
52
+
53
+ echo ""
54
+ echo "✓ Setup complete!"
55
+ echo ""
56
+ echo "Next steps:"
57
+ echo " 1. Restart your terminal (or run: source $PROFILE)"
58
+ echo " 2. Restart Claude Code"
59
+ echo " 3. Try installing Laminark via Claude's UI:"
60
+ echo " /plugin → marketplace → NoobyNull/Laminark → install"
61
+ echo ""
62
+ echo "To verify TMPDIR is set, run: echo \$TMPDIR"
63
+ echo "Expected output: $TMPDIR_PATH"
64
+
65
+ exit 0
@@ -0,0 +1,95 @@
1
+ #!/bin/bash
2
+ # Uninstall Laminark plugin with optional data cleanup
3
+
4
+ set -e
5
+
6
+ echo "Laminark Uninstaller"
7
+ echo "===================="
8
+ echo ""
9
+
10
+ # Check if claude CLI is available
11
+ if ! command -v claude &> /dev/null; then
12
+ echo "Error: claude CLI not found"
13
+ echo "Please install Claude Code first: https://claude.com/claude-code"
14
+ exit 1
15
+ fi
16
+
17
+ # Check if plugin is installed
18
+ if ! claude plugin list 2>/dev/null | grep -q "laminark"; then
19
+ echo "Laminark plugin is not installed."
20
+ exit 0
21
+ fi
22
+
23
+ # Get current version
24
+ CURRENT_VERSION=$(claude plugin list 2>/dev/null | grep "laminark" | awk '{print $2}' || echo "unknown")
25
+ echo "Currently installed: laminark v$CURRENT_VERSION"
26
+ echo ""
27
+
28
+ # Ask for confirmation
29
+ read -p "Are you sure you want to uninstall Laminark? (y/N): " -n 1 -r
30
+ echo ""
31
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
32
+ echo "Uninstall cancelled."
33
+ exit 0
34
+ fi
35
+
36
+ # Remove the plugin
37
+ echo "Removing plugin..."
38
+ claude plugin remove laminark
39
+ echo "✓ Plugin removed"
40
+
41
+ # Ask about data cleanup
42
+ echo ""
43
+ echo "Data cleanup options:"
44
+ echo " 1. Keep all data (can reinstall later without losing memories)"
45
+ echo " 2. Remove plugin cache only (keeps user data at ~/.laminark/)"
46
+ echo " 3. Remove everything (plugin cache + all memories and data)"
47
+ echo ""
48
+ read -p "Choose option (1-3, default=1): " -n 1 -r CLEANUP_OPTION
49
+ echo ""
50
+
51
+ case $CLEANUP_OPTION in
52
+ 2)
53
+ echo "Removing plugin cache..."
54
+ CACHE_DIR="${CLAUDE_HOME:-$HOME/.claude}/plugins/cache/laminark"
55
+ if [ -d "$CACHE_DIR" ]; then
56
+ rm -rf "$CACHE_DIR"
57
+ echo "✓ Plugin cache removed: $CACHE_DIR"
58
+ else
59
+ echo " No cache directory found"
60
+ fi
61
+ ;;
62
+ 3)
63
+ echo "Removing all data..."
64
+ CACHE_DIR="${CLAUDE_HOME:-$HOME/.claude}/plugins/cache/laminark"
65
+ DATA_DIR="$HOME/.laminark"
66
+
67
+ if [ -d "$CACHE_DIR" ]; then
68
+ rm -rf "$CACHE_DIR"
69
+ echo "✓ Plugin cache removed: $CACHE_DIR"
70
+ fi
71
+
72
+ if [ -d "$DATA_DIR" ]; then
73
+ echo ""
74
+ echo "WARNING: This will delete all your memories and observations!"
75
+ read -p "Delete $DATA_DIR? (y/N): " -n 1 -r
76
+ echo ""
77
+ if [[ $REPLY =~ ^[Yy]$ ]]; then
78
+ rm -rf "$DATA_DIR"
79
+ echo "✓ Data directory removed: $DATA_DIR"
80
+ else
81
+ echo " Kept data directory: $DATA_DIR"
82
+ fi
83
+ fi
84
+ ;;
85
+ *)
86
+ echo "Keeping all data."
87
+ ;;
88
+ esac
89
+
90
+ echo ""
91
+ echo "✓ Uninstall complete!"
92
+ echo ""
93
+ echo "To reinstall: ./scripts/local-install.sh or ./scripts/install.sh"
94
+
95
+ exit 0
@@ -0,0 +1,88 @@
1
+ #!/bin/bash
2
+ # Update Laminark plugin to the latest version
3
+
4
+ set -e
5
+
6
+ echo "Laminark Update Checker"
7
+ echo "======================="
8
+ echo ""
9
+
10
+ # Check if claude CLI is available
11
+ if ! command -v claude &> /dev/null; then
12
+ echo "Error: claude CLI not found"
13
+ echo "Please install Claude Code first: https://claude.com/claude-code"
14
+ exit 1
15
+ fi
16
+
17
+ # Check if plugin is installed
18
+ if ! claude plugin list 2>/dev/null | grep -q "laminark"; then
19
+ echo "Laminark is not installed."
20
+ echo "Run ./scripts/install.sh to install it."
21
+ exit 1
22
+ fi
23
+
24
+ # Get current version
25
+ CURRENT_VERSION=$(claude plugin list 2>/dev/null | grep "laminark" | awk '{print $2}' || echo "unknown")
26
+ echo "Currently installed: v$CURRENT_VERSION"
27
+
28
+ # Check for updates (try to get latest version from GitHub)
29
+ echo "Checking for updates..."
30
+ LATEST_VERSION=$(curl -fsSL https://api.github.com/repos/NoobyNull/Laminark/releases/latest 2>/dev/null | grep '"tag_name"' | sed -E 's/.*"v?([^"]+)".*/\1/' || echo "")
31
+
32
+ if [ -z "$LATEST_VERSION" ]; then
33
+ echo "Warning: Could not check for updates (GitHub API unavailable)"
34
+ echo ""
35
+ read -p "Force reinstall anyway? (y/N): " -n 1 -r
36
+ echo ""
37
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
38
+ echo "Update cancelled."
39
+ exit 0
40
+ fi
41
+ FORCE_UPDATE=true
42
+ else
43
+ echo "Latest available: v$LATEST_VERSION"
44
+ echo ""
45
+
46
+ # Compare versions
47
+ if [ "$CURRENT_VERSION" = "$LATEST_VERSION" ]; then
48
+ echo "You already have the latest version!"
49
+ echo ""
50
+ read -p "Reinstall anyway? (y/N): " -n 1 -r
51
+ echo ""
52
+ if [[ ! $REPLY =~ ^[Yy]$ ]]; then
53
+ echo "Update cancelled."
54
+ exit 0
55
+ fi
56
+ fi
57
+ fi
58
+
59
+ # Set up TMPDIR to avoid EXDEV errors
60
+ CLAUDE_DIR="${CLAUDE_HOME:-$HOME/.claude}"
61
+ SAFE_TMP="$CLAUDE_DIR/tmp"
62
+ mkdir -p "$SAFE_TMP"
63
+
64
+ # Reinstall the plugin
65
+ echo ""
66
+ echo "Updating Laminark..."
67
+ TMPDIR="$SAFE_TMP" claude plugin remove laminark 2>/dev/null || true
68
+ TMPDIR="$SAFE_TMP" claude plugin install laminark
69
+ STATUS=$?
70
+
71
+ # Clean up
72
+ rm -rf "$SAFE_TMP"
73
+
74
+ if [ $STATUS -eq 0 ]; then
75
+ NEW_VERSION=$(claude plugin list 2>/dev/null | grep "laminark" | awk '{print $2}' || echo "unknown")
76
+ echo ""
77
+ echo "✓ Laminark updated successfully!"
78
+ echo " Previous version: v$CURRENT_VERSION"
79
+ echo " Current version: v$NEW_VERSION"
80
+ echo ""
81
+ echo "Restart your Claude Code session for changes to take effect."
82
+ else
83
+ echo ""
84
+ echo "✗ Update failed with exit code $STATUS"
85
+ exit $STATUS
86
+ fi
87
+
88
+ exit 0
@@ -0,0 +1,43 @@
1
+ #!/bin/bash
2
+ # Verify Laminark plugin installation
3
+ # Checks: plugin registered, enabled status, provides next steps
4
+
5
+ set -e
6
+
7
+ echo "Checking Laminark installation..."
8
+ echo ""
9
+
10
+ # Check if claude CLI is available
11
+ if ! command -v claude &> /dev/null; then
12
+ echo "✗ Error: claude CLI not found"
13
+ echo " Please install Claude Code first: https://claude.com/claude-code"
14
+ exit 1
15
+ fi
16
+
17
+ # Check if plugin is registered
18
+ if claude plugin list 2>/dev/null | grep -q "laminark"; then
19
+ echo "✓ Plugin registered: laminark"
20
+ else
21
+ echo "✗ Plugin not registered"
22
+ echo " Please run: ./scripts/local-install.sh"
23
+ exit 1
24
+ fi
25
+
26
+ # Check if plugin is enabled
27
+ if claude plugin list 2>/dev/null | grep "laminark" | grep -q "enabled"; then
28
+ echo "✓ Plugin enabled"
29
+ else
30
+ echo "⚠ Plugin registered but not enabled"
31
+ echo " Run: claude plugin enable laminark"
32
+ fi
33
+
34
+ echo ""
35
+ echo "Installation verified successfully!"
36
+ echo ""
37
+ echo "Next steps:"
38
+ echo " 1. Start a new Claude Code session"
39
+ echo " 2. Check MCP tools with: /mcp"
40
+ echo " 3. Check hooks with: /hooks"
41
+ echo " 4. Try searching memories with: recall"
42
+
43
+ exit 0
@@ -0,0 +1,185 @@
1
+ /**
2
+ * Laminark Activity Feed Module
3
+ *
4
+ * Listens to SSE-dispatched CustomEvents and renders a live activity feed.
5
+ * Max 100 items in memory, newest first. Supports clear and slide-in animation.
6
+ */
7
+
8
+ (function () {
9
+ var MAX_ITEMS = 100;
10
+ var items = [];
11
+ var feedEl = null;
12
+
13
+ var EVENT_CONFIG = {
14
+ 'laminark:new_observation': {
15
+ icon: '\u{1F4DD}',
16
+ label: 'Observation',
17
+ cssClass: 'activity-observation',
18
+ format: function (d) {
19
+ return d.text || (d.id ? 'Observation ' + d.id.substring(0, 8) : 'New observation');
20
+ },
21
+ },
22
+ 'laminark:entity_updated': {
23
+ icon: '\u{1F9E9}',
24
+ label: 'Entity',
25
+ cssClass: 'activity-entity',
26
+ format: function (d) {
27
+ return (d.label || d.name || d.id || 'Unknown') + (d.type ? ' (' + d.type + ')' : '');
28
+ },
29
+ },
30
+ 'laminark:topic_shift': {
31
+ icon: '\u{1F500}',
32
+ label: 'Topic Shift',
33
+ cssClass: 'activity-topic-shift',
34
+ format: function (d) {
35
+ var msg = 'Topic shift detected';
36
+ if (d.confidence != null) msg += ' (' + (d.confidence * 100).toFixed(0) + '% confidence)';
37
+ return msg;
38
+ },
39
+ },
40
+ 'laminark:session_start': {
41
+ icon: '\u{25B6}\uFE0F',
42
+ label: 'Session Start',
43
+ cssClass: 'activity-session-start',
44
+ format: function (d) {
45
+ return 'Session started' + (d.id ? ': ' + d.id.substring(0, 8) : '');
46
+ },
47
+ },
48
+ 'laminark:session_end': {
49
+ icon: '\u{23F9}\uFE0F',
50
+ label: 'Session End',
51
+ cssClass: 'activity-session-end',
52
+ format: function (d) {
53
+ return 'Session ended' + (d.id ? ': ' + d.id.substring(0, 8) : '');
54
+ },
55
+ },
56
+ };
57
+
58
+ function relativeTime(isoString) {
59
+ if (!isoString) return 'just now';
60
+ try {
61
+ var diff = Date.now() - new Date(isoString).getTime();
62
+ if (diff < 0) diff = 0;
63
+ var secs = Math.floor(diff / 1000);
64
+ if (secs < 60) return secs + 's ago';
65
+ var mins = Math.floor(secs / 60);
66
+ if (mins < 60) return mins + 'm ago';
67
+ var hrs = Math.floor(mins / 60);
68
+ if (hrs < 24) return hrs + 'h ago';
69
+ return Math.floor(hrs / 24) + 'd ago';
70
+ } catch (_e) {
71
+ return 'just now';
72
+ }
73
+ }
74
+
75
+ function createItemEl(item) {
76
+ var el = document.createElement('div');
77
+ el.className = 'activity-item ' + item.cssClass + ' activity-slide-in';
78
+
79
+ var iconSpan = document.createElement('span');
80
+ iconSpan.className = 'activity-icon';
81
+ iconSpan.textContent = item.icon;
82
+ el.appendChild(iconSpan);
83
+
84
+ var body = document.createElement('div');
85
+ body.className = 'activity-body';
86
+
87
+ var title = document.createElement('div');
88
+ title.className = 'activity-title';
89
+ title.textContent = item.label;
90
+ body.appendChild(title);
91
+
92
+ var desc = document.createElement('div');
93
+ desc.className = 'activity-desc';
94
+ desc.textContent = item.description;
95
+ body.appendChild(desc);
96
+
97
+ el.appendChild(body);
98
+
99
+ var time = document.createElement('span');
100
+ time.className = 'activity-time';
101
+ time.textContent = relativeTime(item.timestamp);
102
+ el.appendChild(time);
103
+
104
+ return el;
105
+ }
106
+
107
+ function renderFeed() {
108
+ if (!feedEl) return;
109
+
110
+ if (items.length === 0) {
111
+ feedEl.innerHTML = '<p class="empty-state">Waiting for live events...</p>';
112
+ return;
113
+ }
114
+
115
+ feedEl.innerHTML = '';
116
+ items.forEach(function (item) {
117
+ feedEl.appendChild(createItemEl(item));
118
+ });
119
+ }
120
+
121
+ function addItem(eventName, detail) {
122
+ var config = EVENT_CONFIG[eventName];
123
+ if (!config) return;
124
+
125
+ var item = {
126
+ icon: config.icon,
127
+ label: config.label,
128
+ cssClass: config.cssClass,
129
+ description: config.format(detail || {}),
130
+ timestamp: detail.createdAt || detail.timestamp || new Date().toISOString(),
131
+ };
132
+
133
+ items.unshift(item);
134
+ if (items.length > MAX_ITEMS) {
135
+ items = items.slice(0, MAX_ITEMS);
136
+ }
137
+
138
+ // If feed is visible, prepend DOM element directly for performance
139
+ if (feedEl) {
140
+ // Remove empty state if present
141
+ var empty = feedEl.querySelector('.empty-state');
142
+ if (empty) empty.remove();
143
+
144
+ var el = createItemEl(item);
145
+ if (feedEl.firstChild) {
146
+ feedEl.insertBefore(el, feedEl.firstChild);
147
+ } else {
148
+ feedEl.appendChild(el);
149
+ }
150
+
151
+ // Trim excess DOM nodes
152
+ while (feedEl.children.length > MAX_ITEMS) {
153
+ feedEl.removeChild(feedEl.lastChild);
154
+ }
155
+ }
156
+ }
157
+
158
+ function clearFeed() {
159
+ items = [];
160
+ renderFeed();
161
+ }
162
+
163
+ function initActivityFeed() {
164
+ feedEl = document.getElementById('activity-feed');
165
+
166
+ var clearBtn = document.getElementById('activity-clear-btn');
167
+ if (clearBtn) {
168
+ clearBtn.addEventListener('click', clearFeed);
169
+ }
170
+
171
+ // Listen to all SSE event types
172
+ Object.keys(EVENT_CONFIG).forEach(function (eventName) {
173
+ document.addEventListener(eventName, function (e) {
174
+ addItem(eventName, e.detail || {});
175
+ });
176
+ });
177
+
178
+ renderFeed();
179
+ }
180
+
181
+ window.laminarkActivity = {
182
+ initActivityFeed: initActivityFeed,
183
+ clearFeed: clearFeed,
184
+ };
185
+ })();