deckbuilder 1.0.0b1__py3-none-any.whl
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.
- deckbuilder/__init__.py +22 -0
- deckbuilder/cli.py +544 -0
- deckbuilder/cli_tools.py +739 -0
- deckbuilder/engine.py +1546 -0
- deckbuilder/image_handler.py +291 -0
- deckbuilder/layout_intelligence.json +288 -0
- deckbuilder/layout_intelligence.py +398 -0
- deckbuilder/naming_conventions.py +541 -0
- deckbuilder/placeholder_types.py +101 -0
- deckbuilder/placekitten_integration.py +280 -0
- deckbuilder/structured_frontmatter.py +862 -0
- deckbuilder/table_styles.py +37 -0
- deckbuilder-1.0.0b1.dist-info/METADATA +378 -0
- deckbuilder-1.0.0b1.dist-info/RECORD +37 -0
- deckbuilder-1.0.0b1.dist-info/WHEEL +5 -0
- deckbuilder-1.0.0b1.dist-info/entry_points.txt +3 -0
- deckbuilder-1.0.0b1.dist-info/licenses/LICENSE +201 -0
- deckbuilder-1.0.0b1.dist-info/top_level.txt +4 -0
- mcp_server/__init__.py +9 -0
- mcp_server/content_analysis.py +436 -0
- mcp_server/content_optimization.py +822 -0
- mcp_server/layout_recommendations.py +595 -0
- mcp_server/main.py +550 -0
- mcp_server/tools.py +492 -0
- placekitten/README.md +561 -0
- placekitten/__init__.py +44 -0
- placekitten/core.py +184 -0
- placekitten/filters.py +183 -0
- placekitten/images/ACuteKitten-1.png +0 -0
- placekitten/images/ACuteKitten-2.png +0 -0
- placekitten/images/ACuteKitten-3.png +0 -0
- placekitten/images/TwoKitttens Playing-1.png +0 -0
- placekitten/images/TwoKitttens Playing-2.png +0 -0
- placekitten/images/TwoKitttensSleeping-1.png +0 -0
- placekitten/processor.py +262 -0
- placekitten/smart_crop.py +314 -0
- shared/__init__.py +9 -0
mcp_server/__init__.py
ADDED
@@ -0,0 +1,436 @@
|
|
1
|
+
"""
|
2
|
+
Content Analysis Module for Deck Builder MCP
|
3
|
+
|
4
|
+
This module implements content-first presentation intelligence, analyzing user input
|
5
|
+
to understand communication goals and recommend presentation structure.
|
6
|
+
|
7
|
+
Design Philosophy: Start with "what does the user want to communicate?"
|
8
|
+
not "what layouts exist?"
|
9
|
+
"""
|
10
|
+
|
11
|
+
import re
|
12
|
+
from typing import Any, Dict, List
|
13
|
+
|
14
|
+
|
15
|
+
class ContentAnalyzer:
|
16
|
+
"""
|
17
|
+
Analyzes user presentation needs using content-first approach.
|
18
|
+
Acts as intelligent presentation consultant, not layout picker.
|
19
|
+
"""
|
20
|
+
|
21
|
+
def __init__(self):
|
22
|
+
"""Initialize the content analyzer with audience profiles."""
|
23
|
+
self.audience_profiles = {
|
24
|
+
"board": {
|
25
|
+
"expertise_level": "expert",
|
26
|
+
"attention_span": "short",
|
27
|
+
"decision_authority": "decision-maker",
|
28
|
+
"preferred_format": "high-level",
|
29
|
+
},
|
30
|
+
"team": {
|
31
|
+
"expertise_level": "intermediate",
|
32
|
+
"attention_span": "medium",
|
33
|
+
"decision_authority": "implementer",
|
34
|
+
"preferred_format": "detailed",
|
35
|
+
},
|
36
|
+
"customers": {
|
37
|
+
"expertise_level": "general",
|
38
|
+
"attention_span": "medium",
|
39
|
+
"decision_authority": "decision-maker",
|
40
|
+
"preferred_format": "visual-heavy",
|
41
|
+
},
|
42
|
+
"technical": {
|
43
|
+
"expertise_level": "expert",
|
44
|
+
"attention_span": "extended",
|
45
|
+
"decision_authority": "influencer",
|
46
|
+
"preferred_format": "detailed",
|
47
|
+
},
|
48
|
+
"general": {
|
49
|
+
"expertise_level": "intermediate",
|
50
|
+
"attention_span": "medium",
|
51
|
+
"decision_authority": "influencer",
|
52
|
+
"preferred_format": "balanced",
|
53
|
+
},
|
54
|
+
}
|
55
|
+
|
56
|
+
def analyze_presentation_needs(
|
57
|
+
self,
|
58
|
+
user_input: str,
|
59
|
+
audience: str = "general",
|
60
|
+
constraints: str = None,
|
61
|
+
presentation_goal: str = "inform",
|
62
|
+
) -> Dict[str, Any]:
|
63
|
+
"""
|
64
|
+
Analyze user's presentation needs and recommend structure.
|
65
|
+
|
66
|
+
Args:
|
67
|
+
user_input: Raw description of what they want to present
|
68
|
+
audience: Target audience type
|
69
|
+
constraints: Time/slide constraints
|
70
|
+
presentation_goal: Primary goal of presentation
|
71
|
+
|
72
|
+
Returns:
|
73
|
+
Dictionary with content analysis and structural recommendations
|
74
|
+
"""
|
75
|
+
# Content Analysis
|
76
|
+
key_messages = self._extract_key_messages(user_input)
|
77
|
+
narrative_arc = self._determine_narrative_arc(user_input, key_messages)
|
78
|
+
complexity_level = self._assess_complexity_level(user_input, audience)
|
79
|
+
data_density = self._analyze_data_density(user_input)
|
80
|
+
emotional_tone = self._detect_emotional_tone(user_input, key_messages)
|
81
|
+
|
82
|
+
# Audience Considerations
|
83
|
+
audience_analysis = self._analyze_audience(audience, constraints)
|
84
|
+
|
85
|
+
# Recommended Structure
|
86
|
+
recommended_structure = self._build_recommended_structure(
|
87
|
+
key_messages, narrative_arc, audience_analysis, presentation_goal
|
88
|
+
)
|
89
|
+
|
90
|
+
# Presentation Strategy
|
91
|
+
presentation_strategy = self._develop_presentation_strategy(
|
92
|
+
narrative_arc, audience_analysis, presentation_goal
|
93
|
+
)
|
94
|
+
|
95
|
+
return {
|
96
|
+
"content_analysis": {
|
97
|
+
"key_messages": key_messages,
|
98
|
+
"narrative_arc": narrative_arc,
|
99
|
+
"complexity_level": complexity_level,
|
100
|
+
"data_density": data_density,
|
101
|
+
"emotional_tone": emotional_tone,
|
102
|
+
},
|
103
|
+
"audience_considerations": audience_analysis,
|
104
|
+
"recommended_structure": recommended_structure,
|
105
|
+
"presentation_strategy": presentation_strategy,
|
106
|
+
}
|
107
|
+
|
108
|
+
def _extract_key_messages(self, user_input: str) -> List[str]:
|
109
|
+
"""Extract key messages from user input using pattern matching."""
|
110
|
+
# Look for metrics, percentages, numbers
|
111
|
+
metrics = re.findall(
|
112
|
+
r"\d+%|\$[\d,]+|\d+[\w\s]*(?:growth|increase|decrease|revenue|users|customers)",
|
113
|
+
user_input.lower(),
|
114
|
+
)
|
115
|
+
|
116
|
+
# Look for key topics/concepts
|
117
|
+
topics = []
|
118
|
+
if re.search(r"growth|expand|increase|up", user_input.lower()):
|
119
|
+
topics.append("growth/expansion")
|
120
|
+
if re.search(r"churn|loss|decrease|down|problem|issue|challenge", user_input.lower()):
|
121
|
+
topics.append("challenges/concerns")
|
122
|
+
if re.search(r"strategy|plan|solution|fix|improve", user_input.lower()):
|
123
|
+
topics.append("solutions/strategy")
|
124
|
+
if re.search(r"new|launch|feature|product", user_input.lower()):
|
125
|
+
topics.append("new initiatives")
|
126
|
+
if re.search(r"market|competitor|industry", user_input.lower()):
|
127
|
+
topics.append("market/competitive")
|
128
|
+
|
129
|
+
# Combine metrics and topics
|
130
|
+
key_messages = []
|
131
|
+
if metrics:
|
132
|
+
key_messages.extend([f"metric: {m}" for m in metrics[:3]]) # Limit to top 3
|
133
|
+
key_messages.extend(topics)
|
134
|
+
|
135
|
+
return key_messages[:5] # Limit to 5 key messages
|
136
|
+
|
137
|
+
def _determine_narrative_arc(self, user_input: str, _key_messages: List[str]) -> str:
|
138
|
+
"""Determine the narrative structure from content."""
|
139
|
+
import re
|
140
|
+
|
141
|
+
input_lower = user_input.lower()
|
142
|
+
|
143
|
+
# Use word boundary matching to avoid false positives
|
144
|
+
def has_word_pattern(patterns, text):
|
145
|
+
"""Check if any pattern exists as whole words in text."""
|
146
|
+
for pattern in patterns:
|
147
|
+
if re.search(r"\b" + re.escape(pattern) + r"\b", text):
|
148
|
+
return True
|
149
|
+
return False
|
150
|
+
|
151
|
+
# Check for success-challenge-solution pattern with word boundaries
|
152
|
+
success_patterns = [
|
153
|
+
"growth",
|
154
|
+
"success",
|
155
|
+
"increase",
|
156
|
+
"expand",
|
157
|
+
"win",
|
158
|
+
"achievement",
|
159
|
+
"improvement",
|
160
|
+
"grew",
|
161
|
+
"expanded",
|
162
|
+
"gains",
|
163
|
+
"positive",
|
164
|
+
]
|
165
|
+
challenge_patterns = [
|
166
|
+
"but",
|
167
|
+
"however",
|
168
|
+
"problem",
|
169
|
+
"issue",
|
170
|
+
"churn",
|
171
|
+
"decrease",
|
172
|
+
"challenge",
|
173
|
+
"concern",
|
174
|
+
"difficulty",
|
175
|
+
"bottleneck",
|
176
|
+
"drops",
|
177
|
+
"causing",
|
178
|
+
]
|
179
|
+
solution_patterns = [
|
180
|
+
"strategy",
|
181
|
+
"plan",
|
182
|
+
"solution",
|
183
|
+
"address",
|
184
|
+
"fix",
|
185
|
+
"propose",
|
186
|
+
"recommend",
|
187
|
+
"implement",
|
188
|
+
"adding",
|
189
|
+
"need",
|
190
|
+
]
|
191
|
+
|
192
|
+
has_success = has_word_pattern(success_patterns, input_lower)
|
193
|
+
has_challenge = has_word_pattern(challenge_patterns, input_lower)
|
194
|
+
has_solution = has_word_pattern(solution_patterns, input_lower)
|
195
|
+
|
196
|
+
if has_success and has_challenge and has_solution:
|
197
|
+
return "success-challenge-solution"
|
198
|
+
elif has_challenge and has_solution:
|
199
|
+
return "problem-solution"
|
200
|
+
elif has_word_pattern(["compare", "vs", "versus", "option", "alternative"], input_lower):
|
201
|
+
return "compare-contrast"
|
202
|
+
elif has_word_pattern(["convince", "persuade", "recommend", "should"], input_lower):
|
203
|
+
return "persuasive"
|
204
|
+
else:
|
205
|
+
return "informational"
|
206
|
+
|
207
|
+
def _assess_complexity_level(self, user_input: str, audience: str) -> str:
|
208
|
+
"""Assess the complexity level needed."""
|
209
|
+
if audience in ["board", "executive"]:
|
210
|
+
return "executive"
|
211
|
+
elif audience == "technical":
|
212
|
+
return "technical"
|
213
|
+
elif len(user_input.split()) > 50 or "detail" in user_input.lower():
|
214
|
+
return "detailed"
|
215
|
+
else:
|
216
|
+
return "overview"
|
217
|
+
|
218
|
+
def _analyze_data_density(self, user_input: str) -> str:
|
219
|
+
"""Analyze how data-heavy the presentation should be."""
|
220
|
+
# Count numeric mentions
|
221
|
+
numeric_mentions = len(re.findall(r"\d+", user_input))
|
222
|
+
|
223
|
+
if numeric_mentions >= 5 or any(
|
224
|
+
word in user_input.lower() for word in ["data", "metrics", "numbers", "statistics"]
|
225
|
+
):
|
226
|
+
return "metrics-heavy"
|
227
|
+
elif any(word in user_input.lower() for word in ["story", "example", "case", "experience"]):
|
228
|
+
return "story-driven"
|
229
|
+
else:
|
230
|
+
return "balanced"
|
231
|
+
|
232
|
+
def _detect_emotional_tone(self, user_input: str, _key_messages: List[str]) -> str:
|
233
|
+
"""Detect the emotional tone of the content."""
|
234
|
+
input_lower = user_input.lower()
|
235
|
+
|
236
|
+
if any(word in input_lower for word in ["urgent", "critical", "immediate", "crisis"]):
|
237
|
+
return "urgent"
|
238
|
+
elif any(
|
239
|
+
word in input_lower for word in ["concern", "worry", "issue", "problem", "challenge"]
|
240
|
+
):
|
241
|
+
return "cautious"
|
242
|
+
elif any(word in input_lower for word in ["success", "growth", "achievement", "win"]):
|
243
|
+
return "positive"
|
244
|
+
else:
|
245
|
+
return "neutral"
|
246
|
+
|
247
|
+
def _analyze_audience(self, audience: str, constraints: str) -> Dict[str, str]:
|
248
|
+
"""Analyze audience characteristics and constraints."""
|
249
|
+
profile = self.audience_profiles.get(audience, self.audience_profiles["general"]).copy()
|
250
|
+
|
251
|
+
# Adjust based on constraints
|
252
|
+
if constraints:
|
253
|
+
if "minutes" in constraints.lower() and any(
|
254
|
+
time in constraints for time in ["5", "10"]
|
255
|
+
):
|
256
|
+
profile["attention_span"] = "short"
|
257
|
+
elif "slides" in constraints.lower():
|
258
|
+
profile["preferred_format"] = "high-level"
|
259
|
+
|
260
|
+
return profile
|
261
|
+
|
262
|
+
def _build_recommended_structure(
|
263
|
+
self,
|
264
|
+
_key_messages: List[str],
|
265
|
+
narrative_arc: str,
|
266
|
+
_audience_analysis: Dict[str, str],
|
267
|
+
_presentation_goal: str,
|
268
|
+
) -> List[Dict[str, str]]:
|
269
|
+
"""Build recommended slide structure."""
|
270
|
+
structure = []
|
271
|
+
|
272
|
+
if narrative_arc == "success-challenge-solution":
|
273
|
+
structure = [
|
274
|
+
{
|
275
|
+
"position": 1,
|
276
|
+
"purpose": "lead with strength",
|
277
|
+
"content_focus": "positive metrics/achievements",
|
278
|
+
"slide_intent": "establish credibility",
|
279
|
+
"estimated_time": "1-2 minutes",
|
280
|
+
},
|
281
|
+
{
|
282
|
+
"position": 2,
|
283
|
+
"purpose": "show momentum",
|
284
|
+
"content_focus": "expansion/progress",
|
285
|
+
"slide_intent": "build on success",
|
286
|
+
"estimated_time": "1-2 minutes",
|
287
|
+
},
|
288
|
+
{
|
289
|
+
"position": 3,
|
290
|
+
"purpose": "acknowledge challenge",
|
291
|
+
"content_focus": "problem/concern",
|
292
|
+
"slide_intent": "transparent communication",
|
293
|
+
"estimated_time": "2-3 minutes",
|
294
|
+
},
|
295
|
+
{
|
296
|
+
"position": 4,
|
297
|
+
"purpose": "present solution",
|
298
|
+
"content_focus": "strategy/plan",
|
299
|
+
"slide_intent": "show path forward",
|
300
|
+
"estimated_time": "2-3 minutes",
|
301
|
+
},
|
302
|
+
]
|
303
|
+
elif narrative_arc == "problem-solution":
|
304
|
+
structure = [
|
305
|
+
{
|
306
|
+
"position": 1,
|
307
|
+
"purpose": "set context",
|
308
|
+
"content_focus": "background/situation",
|
309
|
+
"slide_intent": "establish understanding",
|
310
|
+
"estimated_time": "1-2 minutes",
|
311
|
+
},
|
312
|
+
{
|
313
|
+
"position": 2,
|
314
|
+
"purpose": "define problem",
|
315
|
+
"content_focus": "challenges/issues",
|
316
|
+
"slide_intent": "create urgency",
|
317
|
+
"estimated_time": "2-3 minutes",
|
318
|
+
},
|
319
|
+
{
|
320
|
+
"position": 3,
|
321
|
+
"purpose": "present solution",
|
322
|
+
"content_focus": "strategy/approach",
|
323
|
+
"slide_intent": "provide resolution",
|
324
|
+
"estimated_time": "3-4 minutes",
|
325
|
+
},
|
326
|
+
]
|
327
|
+
elif narrative_arc == "compare-contrast":
|
328
|
+
structure = [
|
329
|
+
{
|
330
|
+
"position": 1,
|
331
|
+
"purpose": "establish criteria",
|
332
|
+
"content_focus": "comparison framework",
|
333
|
+
"slide_intent": "set evaluation context",
|
334
|
+
"estimated_time": "1-2 minutes",
|
335
|
+
},
|
336
|
+
{
|
337
|
+
"position": 2,
|
338
|
+
"purpose": "present options",
|
339
|
+
"content_focus": "alternatives/choices",
|
340
|
+
"slide_intent": "show possibilities",
|
341
|
+
"estimated_time": "3-4 minutes",
|
342
|
+
},
|
343
|
+
{
|
344
|
+
"position": 3,
|
345
|
+
"purpose": "recommend choice",
|
346
|
+
"content_focus": "preferred option",
|
347
|
+
"slide_intent": "guide decision",
|
348
|
+
"estimated_time": "2-3 minutes",
|
349
|
+
},
|
350
|
+
]
|
351
|
+
else: # informational/other
|
352
|
+
structure = [
|
353
|
+
{
|
354
|
+
"position": 1,
|
355
|
+
"purpose": "overview",
|
356
|
+
"content_focus": "main topics",
|
357
|
+
"slide_intent": "set expectations",
|
358
|
+
"estimated_time": "1-2 minutes",
|
359
|
+
},
|
360
|
+
{
|
361
|
+
"position": 2,
|
362
|
+
"purpose": "key information",
|
363
|
+
"content_focus": "primary content",
|
364
|
+
"slide_intent": "deliver core message",
|
365
|
+
"estimated_time": "3-4 minutes",
|
366
|
+
},
|
367
|
+
{
|
368
|
+
"position": 3,
|
369
|
+
"purpose": "summary",
|
370
|
+
"content_focus": "takeaways",
|
371
|
+
"slide_intent": "reinforce learning",
|
372
|
+
"estimated_time": "1-2 minutes",
|
373
|
+
},
|
374
|
+
]
|
375
|
+
|
376
|
+
return structure
|
377
|
+
|
378
|
+
def _develop_presentation_strategy(
|
379
|
+
self, narrative_arc: str, audience_analysis: Dict[str, str], _presentation_goal: str
|
380
|
+
) -> Dict[str, Any]:
|
381
|
+
"""Develop presentation strategy."""
|
382
|
+
strategy = {
|
383
|
+
"opening_approach": "data-driven",
|
384
|
+
"closing_approach": "summary",
|
385
|
+
"flow_pattern": "chronological",
|
386
|
+
"engagement_tactics": ["visual"],
|
387
|
+
}
|
388
|
+
|
389
|
+
# Adjust based on narrative arc
|
390
|
+
if narrative_arc == "success-challenge-solution":
|
391
|
+
strategy["opening_approach"] = "data-driven"
|
392
|
+
strategy["flow_pattern"] = "problem-solution"
|
393
|
+
strategy["closing_approach"] = "call-to-action"
|
394
|
+
elif narrative_arc == "problem-solution":
|
395
|
+
strategy["opening_approach"] = "problem-focused"
|
396
|
+
strategy["flow_pattern"] = "problem-solution"
|
397
|
+
strategy["closing_approach"] = "next-steps"
|
398
|
+
elif narrative_arc == "persuasive":
|
399
|
+
strategy["opening_approach"] = "story-based"
|
400
|
+
strategy["closing_approach"] = "call-to-action"
|
401
|
+
strategy["engagement_tactics"].append("storytelling")
|
402
|
+
elif narrative_arc == "compare-contrast":
|
403
|
+
strategy["opening_approach"] = "framework-based"
|
404
|
+
strategy["flow_pattern"] = "compare-contrast"
|
405
|
+
strategy["closing_approach"] = "recommendation"
|
406
|
+
|
407
|
+
# Adjust based on audience
|
408
|
+
if audience_analysis["attention_span"] == "short":
|
409
|
+
strategy["engagement_tactics"].append("interactive")
|
410
|
+
if audience_analysis["preferred_format"] == "visual-heavy":
|
411
|
+
strategy["engagement_tactics"] = ["visual", "interactive"]
|
412
|
+
|
413
|
+
return strategy
|
414
|
+
|
415
|
+
|
416
|
+
# Helper function for easy import
|
417
|
+
def analyze_presentation_needs(
|
418
|
+
user_input: str,
|
419
|
+
audience: str = "general",
|
420
|
+
constraints: str = None,
|
421
|
+
presentation_goal: str = "inform",
|
422
|
+
) -> Dict[str, Any]:
|
423
|
+
"""
|
424
|
+
Convenience function for analyzing presentation needs.
|
425
|
+
|
426
|
+
Args:
|
427
|
+
user_input: Raw description of what they want to present
|
428
|
+
audience: Target audience type
|
429
|
+
constraints: Time/slide constraints
|
430
|
+
presentation_goal: Primary goal of presentation
|
431
|
+
|
432
|
+
Returns:
|
433
|
+
Dictionary with content analysis and structural recommendations
|
434
|
+
"""
|
435
|
+
analyzer = ContentAnalyzer()
|
436
|
+
return analyzer.analyze_presentation_needs(user_input, audience, constraints, presentation_goal)
|