Merge branch 'animations'
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,2 +1,3 @@
|
||||
**/*.pyc
|
||||
tmp/**/*
|
||||
.serena/cache/**
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
{
|
||||
"mcpServers": {
|
||||
"serena": {
|
||||
"command": "uvx",
|
||||
"args": ["--from", "git+https://github.com/oraios/serena", "serena", "start-mcp-server", "--context", "ide-assistant", "--project", "/home/notid/dev/email-organizer"]
|
||||
},
|
||||
"context7": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
|
||||
@@ -15,6 +15,7 @@ Here are special rules you must follow:
|
||||
13. Plans go into docs/plans/*.md. These may not be kept in sync, as they are just for brainstorming.
|
||||
14. ****IMPORTANT**** Database migrations are automatically created via `flask db migrate -m 'message'`. **NEVER** create migrations by hand. You should never have to read the contents of migrations/
|
||||
15. In the technical documentation, code should be used sparingly. Also, when needed, focus on the APIs, and only use snippets.
|
||||
16. If appropriate context has been given in the prompt for the task at hand, don't read the whole file.
|
||||
|
||||
|
||||
# Conventions
|
||||
|
||||
44
.roomodes
Normal file
44
.roomodes
Normal file
@@ -0,0 +1,44 @@
|
||||
customModes:
|
||||
- slug: user-story-creator
|
||||
name: 📝 User Story Creator
|
||||
roleDefinition: |
|
||||
You are an agile requirements specialist focused on creating clear, valuable user stories. Your expertise includes:
|
||||
- Crafting well-structured user stories following the standard format
|
||||
- Breaking down complex requirements into manageable stories
|
||||
- Identifying acceptance criteria and edge cases
|
||||
- Ensuring stories deliver business value
|
||||
- Maintaining consistent story quality and granularity
|
||||
whenToUse: |
|
||||
Use this mode when you need to create user stories, break down requirements into manageable pieces, or define acceptance criteria for features. Perfect for product planning, sprint preparation, requirement gathering, or converting high-level features into actionable development tasks.
|
||||
description: Create structured agile user stories
|
||||
groups:
|
||||
- read
|
||||
- edit
|
||||
- command
|
||||
source: project
|
||||
customInstructions: |
|
||||
Expected User Story Format:
|
||||
|
||||
Title: [Brief descriptive title]
|
||||
|
||||
As a [specific user role/persona],
|
||||
I want to [clear action/goal],
|
||||
So that [tangible benefit/value].
|
||||
|
||||
Acceptance Criteria:
|
||||
1. [Criterion 1]
|
||||
2. [Criterion 2]
|
||||
3. [Criterion 3]
|
||||
|
||||
Story Types to Consider:
|
||||
- Functional Stories (user interactions and features)
|
||||
- Non-functional Stories (performance, security, usability)
|
||||
- Epic Breakdown Stories (smaller, manageable pieces)
|
||||
- Technical Stories (architecture, infrastructure)
|
||||
|
||||
Edge Cases and Considerations:
|
||||
- Error scenarios
|
||||
- Permission levels
|
||||
- Data validation
|
||||
- Performance requirements
|
||||
- Security implications
|
||||
@@ -11,10 +11,33 @@ login_manager.login_view = 'auth.login'
|
||||
login_manager.login_message = 'Please log in to access this page.'
|
||||
login_manager.login_message_category = 'warning'
|
||||
|
||||
from flask import Flask, request, make_response
|
||||
from flask_sqlalchemy import SQLAlchemy
|
||||
from flask_login import LoginManager
|
||||
from config import config
|
||||
from app.models import db, Base, User
|
||||
from flask_migrate import Migrate
|
||||
|
||||
# Initialize Flask-Login
|
||||
login_manager = LoginManager()
|
||||
login_manager.login_view = 'auth.login'
|
||||
login_manager.login_message = 'Please log in to access this page.'
|
||||
login_manager.login_message_category = 'warning'
|
||||
|
||||
|
||||
def create_app(config_name='default'):
|
||||
app = Flask(__name__, static_folder='static', static_url_path='/static')
|
||||
app.config.from_object(config[config_name])
|
||||
|
||||
# Add middleware to simulate 500 errors
|
||||
@app.before_request
|
||||
def check_for_simulate_500():
|
||||
# Check if the X-Simulate-500 header is present
|
||||
if request.headers.get('X-Simulate-500'):
|
||||
response = make_response({'error': 'Simulated server error'}, 500)
|
||||
response.headers['Content-Type'] = 'application/json'
|
||||
return response
|
||||
|
||||
# Initialize extensions
|
||||
db.init_app(app)
|
||||
migrate = Migrate(app, db)
|
||||
|
||||
@@ -13,6 +13,37 @@
|
||||
<script src="https://cdn.jsdelivr.net/npm/@ryangjchandler/alpine-tooltip@1.x.x/dist/cdn.min.js" defer></script>
|
||||
<link rel="stylesheet" href="https://unpkg.com/tippy.js@6/dist/tippy.css" />
|
||||
<script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.x.x/dist/cdn.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('alpine:init', () => {
|
||||
Alpine.store('errorToast', {
|
||||
show: false,
|
||||
message: '',
|
||||
hide() {
|
||||
this.show = false;
|
||||
},
|
||||
handleError(detail) {
|
||||
|
||||
if (detail.xhr.status >= 500 && detail.xhr.status < 600) {
|
||||
// Extract error message from response
|
||||
let errorMessage = 'Server Error';
|
||||
try {
|
||||
const responseJson = JSON.parse(detail.xhr.response);
|
||||
if (responseJson.error) {
|
||||
errorMessage = responseJson.error;
|
||||
}
|
||||
} catch (e) {
|
||||
// If not JSON, use the raw response
|
||||
errorMessage = detail.xhr.response || 'Server Error';
|
||||
}
|
||||
|
||||
// Set error message and show toast using Alpine store
|
||||
this.message = errorMessage;
|
||||
this.show = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style>
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
@@ -48,14 +79,33 @@
|
||||
</style>
|
||||
{% block head %}{% endblock %}
|
||||
</head>
|
||||
<body class="min-h-screen flex flex-col" x-data="{}" x-on:open-modal.window="$refs.modal.showModal()"
|
||||
x-on:close-modal.window="$refs.modal.close()"
|
||||
hx-ext="loading-states"
|
||||
data-loading-delay="200">
|
||||
<body
|
||||
x-on:htmx:response-error.camel="$store.errorToast.handleError($event.detail)"
|
||||
class="min-h-screen flex flex-col" x-data="{}" x-on:open-modal.window="$refs.modal.showModal()"
|
||||
x-on:close-modal.window="$refs.modal.close()" hx-ext="loading-states" data-loading-delay="200">
|
||||
{% block header %}{% endblock %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
|
||||
{% block modal %}{% endblock %}
|
||||
|
||||
<!-- Toast for 5xx errors -->
|
||||
<div id="error-toast" class="toast toast-top toast-end w-full z-100" x-show="$store.errorToast.show" x-transition.duration.200ms>
|
||||
<div class="alert alert-warning backdrop-blur-md bg-error/30 border border-error/20 relative">
|
||||
|
||||
<span id="error-message" x-text="$store.errorToast.message"></span>
|
||||
<button class="btn btn-sm btn-ghost absolute top-2 right-2" @click="$store.errorToast.hide()">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
// Check for global variable to set X-Simulate-500 header
|
||||
document.addEventListener('htmx:configRequest', function (evt) {
|
||||
if (typeof window.simulate500 === 'boolean' && window.simulate500) {
|
||||
evt.detail.headers['X-Simulate-500'] = 'true';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user