emile 10 months ago
parent ccbec47cab
commit 988ac3a2d7

@ -41,16 +41,6 @@ class Ticket(models.Model):
super().save(*args, **kwargs)
class TicketStatus(models.Model):
STATUS_CHOICES = (
('Open', 'Open'),
('Working On', 'Working On'),
('Closed', 'Closed'),
)
ticket = models.ForeignKey(Ticket, on_delete=models.CASCADE)
status = models.CharField(max_length=50, choices=STATUS_CHOICES, null=True)
added_by = models.ForeignKey(User, on_delete=models.SET_NULL, null=True)
date_added = models.DateTimeField()
class TicketStatus(models.Model):
STATUS_CHOICES = (
('Open', 'Open'),
@ -80,7 +70,7 @@ class TicketAttachment(models.Model):
ticket_update = models.ForeignKey(TicketUpdate, on_delete=models.CASCADE, null=True, blank=True)
file_path = models.CharField(max_length=255, null=True)
class TicketUpdateReaction(models.Model):
REACTION_CHOICES = (
('Happy', 'Happy'),

Binary file not shown.

Binary file not shown.

@ -0,0 +1,99 @@
from channels.generic.websocket import WebsocketConsumer
from django.shortcuts import get_object_or_404
from customercore.models import *
import json
from django.template.loader import render_to_string
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumer
from django.shortcuts import get_object_or_404
from customercore.models import *
import json
from django.template.loader import render_to_string
from asgiref.sync import async_to_sync
class TicketRoomConsumer(WebsocketConsumer):
def connect(self):
self.user = self.scope['user']
self.ticket_id = self.scope['url_route']['kwargs']['ticket_id']
self.ticket = get_object_or_404(Ticket, id=self.ticket_id)
self.ticket_number = self.ticket.ticket_number
async_to_sync(self.channel_layer.group_add)(
self.ticket_number, self.channel_name
)
self.accept()
def disconnect(self, close_code):
async_to_sync(self.channel_layer.group_discard)(
self.ticket_number, self.channel_name
)
def receive(self, text_data):
text_data_json = json.loads(text_data)
event_type = text_data_json.get('event_type')
if event_type == 'typing':
event = {
'type': 'typing_handler',
'user': self.scope['user']
}
async_to_sync(self.channel_layer.group_send)(
self.ticket_number, event
)
elif event_type == 'stop_typing':
event = {
'type': 'stop_typing_handler',
}
async_to_sync(self.channel_layer.group_send)(
self.ticket_number, event
)
else:
body = text_data_json['description']
file_paths = text_data_json['filePath']
ticketupdate = TicketUpdate.objects.create(
added_by=self.user,
description=body,
ticket=self.ticket,
date_added=datetime.now()
)
for file_path in file_paths:
ticket_attachment = TicketAttachment(
ticket_update=ticketupdate,
file_path=file_path
)
ticket_attachment.save()
event = {
'type': 'update_handler',
'update_id': ticketupdate.id
}
async_to_sync(self.channel_layer.group_send)(
self.ticket_number, event
)
def update_handler(self, event):
update_id = event['update_id']
update = TicketUpdate.objects.get(id=update_id)
context = {
'update': update,
'user': self.user
}
html = render_to_string("details_templates/new-ticket-message.html", context=context)
self.send(text_data=json.dumps({
'event_type': 'update',
'html': html
}))
def typing_handler(self, event):
context = {
'user': event['user']
}
html = render_to_string("details_templates/typing-message.html", context=context)
self.send(text_data=json.dumps({
'event_type': 'typing',
'html': html
}))
def stop_typing_handler(self, event):
self.send(text_data=json.dumps({
'event_type': 'stop_typing'
}))

@ -211,10 +211,6 @@ class PinnedProject(models.Model):
project = models.ForeignKey(Project, on_delete=models.CASCADE)
class PinnedProject(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
project = models.ForeignKey(Project, on_delete=models.CASCADE)
class Milestone(models.Model):

@ -0,0 +1,8 @@
from django.urls import path
from .consumers import *
websocket_urlpatterns = [
path("ws/ticketroom/<int:ticket_id>/", TicketRoomConsumer.as_asgi()),
]

Binary file not shown.

@ -0,0 +1,19 @@
<div id="messages" hx-swap-oob="beforeend">
<div class="fade-in-up">
{% include 'details_templates/ticket-message.html' %}
</div>
<style>
@keyframes fadeInAndUp {
from { opacity: 0; transform: translateY(12px); }
to { opacity: 1; transform: translateY(0px); }
}
.fade-in-up {
animation: fadeInAndUp 0.6s ease;
}
</style>
</div>

@ -7,7 +7,9 @@
@import url(https://cdnjs.cloudflare.com/ajax/libs/MaterialDesign-Webfont/5.3.45/css/materialdesignicons.min.css);
</style>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.js" defer></script>
<script src="https://unpkg.com/htmx.org/dist/htmx.js"></script>
<script src="https://unpkg.com/htmx.org/dist/ext/ws.js"></script>
<script src="https://unpkg.com/hyperscript.org@0.9.12"></script>
<div class="w-full xxlg1:w-[75%] bg-white h-fit rounded-md shadow-md p-5">
@ -72,83 +74,20 @@
</div>
<div class="w-full flex flex-col gap-8 mt-5 s:mt-10">
<div class="w-full flex flex-col mt-5 s:mt-10">
<!-- REPLY 1 -->
{% for update in ticket_updates %}
<div class="flex gap-3">
<div>
<div class="w-[45px] s:w-[60px] h-[45px] s:h-[60px] rounded-full shadow-md border border-gray-100">
{% if update.added_by.customerprofile %}
{% if update.added_by.customerprofile.image %}
<img src="{{update.added_by.customerprofile.image.url}}"
class="w-full h-full rounded-full object-cover">
{% else %}
<div
class="w-full h-full border border-secondosiblue bg-secondosiblue text-white uppercase rounded-full flex justify-center items-center p-1 shadow-md">
{{ update.added_by.first_name.0 }}{{ update.added_by.last_name.0 }}
</div>
{% endif %}
{% elif update.added_by.staffprofile %}
{% if update.added_by.staffprofile.image %}
<img src="{{update.added_by.staffprofile.image.url}}"
class="w-full h-full rounded-full object-cover">
{% else %}
<div
class="w-full h-full border border-osiblue bg-osiblue text-white uppercase rounded-full flex justify-center items-center p-1 shadow-md">
{{ update.added_by.first_name.0 }}{{ update.added_by.last_name.0 }}
</div>
{% endif %}
{% endif %}
</div>
</div>
<div class="w-full replyContainer shadow-md">
<div
class="w-full bg-gray-100 flex justify-between items-center gap-3 px-3 py-3 cursor-pointer rounded-t-md toggleReply">
<p class="text-secondosiblue font-light text-sm s:text-base"><span
class="font-semibold">{{update.added_by.first_name}}</span>
replied {{update.date_added}}</p>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-4 h-4 text-secondosiblue arrowUp">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-4 h-4 text-secondosiblue arrowDown hidden">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
</div>
<div class="w-full bg-white p-5 flex flex-col gap-3 reply default-css">
{{update.description | safe }}
{% if update.ticketattachment_set.all %}
<div class="w-full flex flex-wrap justify-end items-center gap-3">
{% for file in update.ticketattachment_set.all %}
<div
class="flex items-center gap-1 text-secondosiblue hover:text-gray-500 duration-300 cursor-pointer text-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="w-4 h-4 text-secondosiblue">
<path stroke-linecap="round" stroke-linejoin="round"
d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
</svg>
<a href="https://osina.ositcom.com/{{file.file_path}}" target="_blank">{{ file.file_path | cut:"static/images/uploaded_ticket_files/" }}{% if not forloop.last %}, {% endif %}</a>
</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
<div id="messages">
{% for update in ticket_updates %}
{% include 'details_templates/ticket-message.html' %}
{% endfor %}
</div>
{% endfor %}
<div id="typing-notification"></div>
<!-- REPLYING SECTION -->
<form class="flex gap-3" method="POST" action="{% url 'addticketupdate' ticket.id %}"
<form id="ticketForm" class="flex gap-3"
enctype="multipart/form-data">
{% csrf_token %}
<div>
@ -178,104 +117,9 @@
<div class="w-full flex flex-col gap-3">
<div class="w-full rounded-md text-black" x-data="app()"
x-init="init($refs.wysiwyg)">
<div class="border border-gray-200 overflow-hidden rounded-md">
<div class="w-full border-b border-gray-200 flex flex-wrap text-xl text-gray-600">
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('bold')">
<i class="mdi mdi-format-bold"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('italic')">
<i class="mdi mdi-format-italic"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('underline')">
<i class="mdi mdi-format-underline"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('formatBlock','P')">
<i class="mdi mdi-format-paragraph"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('formatBlock','H1')">
<i class="mdi mdi-format-header-1"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('formatBlock','H2')">
<i class="mdi mdi-format-header-2"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('formatBlock','H3')">
<i class="mdi mdi-format-header-3"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('insertUnorderedList')">
<i class="mdi mdi-format-list-bulleted"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
@click="format('insertOrderedList')">
<i class="mdi mdi-format-list-numbered"></i>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
id="rightToLeft" @click="setDirection('rtl')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-[18px] h-[18px]"
fill="none">
<path d="M15 3.5H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M15 9.5H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M3 15.5H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M3 21.5H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
<path
d="M3.58579 9.91421C4.17157 10.5 5.11438 10.5 7 10.5C8.88562 10.5 9.82843 10.5 10.4142 9.91421C11 9.32843 11 8.38562 11 6.5C11 4.61438 11 3.67157 10.4142 3.08579C9.82843 2.5 8.88562 2.5 7 2.5C5.11438 2.5 4.17157 2.5 3.58579 3.08579C3 3.67157 3 4.61438 3 6.5C3 8.38562 3 9.32843 3.58579 9.91421Z"
stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</button>
<button type="button"
class="w-[40px] h-[40px] border-r border-gray-200 flex justify-center items-center text-gray-400 duration-300 cursor-pointer hover:text-secondosiblue"
id="leftToLeft" @click="setDirection('ltr')">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="w-[18px] h-[18px]"
fill="none">
<path d="M3 3.5H9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M3 9.5H9" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M3 15.5H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
<path d="M3 21.5H21" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
<path
d="M13.5858 9.91421C14.1716 10.5 15.1144 10.5 17 10.5C18.8856 10.5 19.8284 10.5 20.4142 9.91421C21 9.32843 21 8.38562 21 6.5C21 4.61438 21 3.67157 20.4142 3.08579C19.8284 2.5 18.8856 2.5 17 2.5C15.1144 2.5 14.1716 2.5 13.5858 3.08579C13 3.67157 13 4.61438 13 6.5C13 8.38562 13 9.32843 13.5858 9.91421Z"
stroke="currentColor" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round" />
</svg>
</button>
</div>
<div class="w-full">
<iframe x-ref="wysiwyg" class="w-full h-[300px] overflow-y-auto"></iframe>
</div>
</div>
</div>
<textarea name="description" rows="8" id="textEditor"
class="w-full bg-white px-3 py-3 border border-gray-200 rounded-b-md outline-none text-gray-500 resize-none hidden"
<textarea name="description" rows="8" id="textEditor"
class="w-full bg-white px-3 py-3 border border-gray-200 rounded-b-md outline-none text-gray-500 resize-none"
placeholder="Add Comment..." required></textarea>
<div class="w-full flex flex-col items-end gap-3">
@ -308,7 +152,92 @@
</div>
</div>
</form>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const ticketId = '{{ticket.id}}'; // Use the appropriate way to pass the ticket ID
const wsUrl = `ws://${window.location.host}/ws/ticketroom/${ticketId}/`;
const socket = new WebSocket(wsUrl);
let typingTimeout = null;
socket.onopen = () => {
console.log('WebSocket connection established');
};
socket.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.event_type === 'typing') {
const typingDiv = document.getElementById('typing-notification');
typingDiv.innerHTML = data.html;
} else if (data.event_type === 'stop_typing') {
const typingDiv = document.getElementById('typing-notification');
typingDiv.innerHTML = '';
} else {
const typingDiv = document.getElementById('typing-notification');
const messagesDiv = document.getElementById('messages');
messagesDiv.insertAdjacentHTML('beforeend', data.html);
typingDiv.innerHTML = '';
}
};
socket.onclose = () => {
console.log('WebSocket connection closed');
};
socket.onerror = (error) => {
console.log('WebSocket error:', error);
};
const form = document.getElementById('ticketForm');
const textEditor = document.getElementById('textEditor');
textEditor.addEventListener('input', () => {
clearTimeout(typingTimeout);
const data = {
event_type: 'typing'
};
socket.send(JSON.stringify(data));
typingTimeout = setTimeout(() => {
const stopTypingData = {
event_type: 'stop_typing'
};
socket.send(JSON.stringify(stopTypingData));
}, 2000);
});
form.addEventListener('submit', (event) => {
event.preventDefault();
const formData = new FormData(form);
const description = formData.get('description');
const files = formData.getAll('filePath');
const filePaths = [];
for (const file of files) {
const filePath = `/path/to/uploaded/files/${file.name}`;
filePaths.push(filePath);
}
const data = {
event_type: 'update',
description: description,
filePath: filePaths
};
socket.send(JSON.stringify(data));
form.reset();
});
});
</script>
<div class="w-full mt-5">
@ -427,6 +356,7 @@
</div>
<!---------------------- JS SCRIPTS -------------------->
<script type="text/javascript" src="{% static 'js/tickets/ticket-details.js' %}"></script>
@ -434,4 +364,5 @@
<script type="text/javascript" src='{% static "js/inputs/file-uploader.js" %}'></script>
{% endblock %}

@ -0,0 +1,69 @@
<div class="flex gap-3 mb-10">
<div>
<div class="w-[45px] s:w-[60px] h-[45px] s:h-[60px] rounded-full shadow-md border border-gray-100">
{% if update.added_by.customerprofile %}
{% if update.added_by.customerprofile.image %}
<img src="{{update.added_by.customerprofile.image.url}}"
class="w-full h-full rounded-full object-cover">
{% else %}
<div
class="w-full h-full border border-secondosiblue bg-secondosiblue text-white uppercase rounded-full flex justify-center items-center p-1 shadow-md">
{{ update.added_by.first_name.0 }}{{ update.added_by.last_name.0 }}
</div>
{% endif %}
{% elif update.added_by.staffprofile %}
{% if update.added_by.staffprofile.image %}
<img src="{{update.added_by.staffprofile.image.url}}"
class="w-full h-full rounded-full object-cover">
{% else %}
<div
class="w-full h-full border border-osiblue bg-osiblue text-white uppercase rounded-full flex justify-center items-center p-1 shadow-md">
{{ update.added_by.first_name.0 }}{{ update.added_by.last_name.0 }}
</div>
{% endif %}
{% endif %}
</div>
</div>
<div class="w-full replyContainer shadow-md">
<div
class="w-full bg-gray-100 flex justify-between items-center gap-3 px-3 py-3 cursor-pointer rounded-t-md toggleReply">
<p class="text-secondosiblue font-light text-sm s:text-base"><span
class="font-semibold">{{update.added_by.first_name}}</span>
replied {{update.date_added}}</p>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-4 h-4 text-secondosiblue arrowUp">
<path stroke-linecap="round" stroke-linejoin="round" d="m4.5 15.75 7.5-7.5 7.5 7.5" />
</svg>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
stroke="currentColor" class="w-4 h-4 text-secondosiblue arrowDown hidden">
<path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
</svg>
</div>
<div class="w-full bg-white p-5 flex flex-col gap-3 reply default-css">
{{update.description | safe }}
{% if update.ticketattachment_set.all %}
<div class="w-full flex flex-wrap justify-end items-center gap-3">
{% for file in update.ticketattachment_set.all %}
<div
class="flex items-center gap-1 text-secondosiblue hover:text-gray-500 duration-300 cursor-pointer text-sm">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"
stroke-width="1.5" stroke="currentColor" class="w-4 h-4 text-secondosiblue">
<path stroke-linecap="round" stroke-linejoin="round"
d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
</svg>
<a href="https://osina.ositcom.com/{{file.file_path}}" target="_blank">{{ file.file_path | cut:"static/images/uploaded_ticket_files/" }}{% if not forloop.last %}, {% endif %}</a>
</div>
{% endfor %}
</div>
{% endif %}
</div>
</div>
</div>

@ -0,0 +1,36 @@
<div id="typing-notification" class="flex gap-3 mb-10">
<div>
<div class="w-[45px] s:w-[60px] h-[45px] s:h-[60px] rounded-full shadow-md border border-gray-100">
{% if user.customerprofile %}
{% if user.customerprofile.image %}
<img src="{{user.customerprofile.image.url}}"
class="w-full h-full rounded-full object-cover">
{% else %}
<div
class="w-full h-full border border-secondosiblue bg-secondosiblue text-white uppercase rounded-full flex justify-center items-center p-1 shadow-md">
{{ user.first_name.0 }}{{ user.last_name.0 }}
</div>
{% endif %}
{% elif user.staffprofile %}
{% if user.staffprofile.image %}
<img src="{{user.staffprofile.image.url}}"
class="w-full h-full rounded-full object-cover">
{% else %}
<div
class="w-full h-full border border-osiblue bg-osiblue text-white uppercase rounded-full flex justify-center items-center p-1 shadow-md">
{{ user.first_name.0 }}{{ user.last_name.0 }}
</div>
{% endif %}
{% endif %}
</div>
</div>
<div class="w-full replyContainer shadow-md">
<div
class="w-full bg-gray-100 flex justify-between items-center gap-3 px-3 py-3 cursor-pointer rounded-t-md toggleReply">
<p class="text-secondosiblue font-light text-sm s:text-base"><span
class="font-semibold">{{user.first_name}}</span> is typing... </p>
</div>
</div>
</div>

@ -10,7 +10,21 @@ https://docs.djangoproject.com/en/4.1/howto/deployment/asgi/
import os
from django.core.asgi import get_asgi_application
from channels.routing import ProtocolTypeRouter, URLRouter
from channels.security.websocket import AllowedHostsOriginValidator
from channels.auth import AuthMiddlewareStack
from osinacore import routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'osinaweb.settings')
application = get_asgi_application()
django_asgi_app = get_asgi_application()
application = ProtocolTypeRouter({
"http": django_asgi_app,
"websocket": AllowedHostsOriginValidator(
AuthMiddlewareStack(URLRouter(routing.websocket_urlpatterns))
),
})

@ -40,6 +40,7 @@ LOGIN_URL = 'signin'
# Application definition
INSTALLED_APPS = [
'daphne',
'rest_framework',
'osinacore',
'customercore',
@ -89,7 +90,13 @@ TEMPLATES = [
},
]
WSGI_APPLICATION = 'osinaweb.wsgi.application'
ASGI_APPLICATION = 'osinaweb.asgi.application'
CHANNEL_LAYERS = {
'default':{
"BACKEND": "channels.layers.InMemoryChannelLayer",
}
}
# Database

@ -1,5 +1,5 @@
/*
! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com
! tailwindcss v3.3.3 | MIT License | https://tailwindcss.com
*/
/*
@ -32,11 +32,9 @@
4. Use the user's configured `sans` font-family by default.
5. Use the user's configured `sans` font-feature-settings by default.
6. Use the user's configured `sans` font-variation-settings by default.
7. Disable tap highlights on iOS
*/
html,
:host {
html {
line-height: 1.5;
/* 1 */
-webkit-text-size-adjust: 100%;
@ -46,14 +44,12 @@ html,
-o-tab-size: 4;
tab-size: 4;
/* 3 */
font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
/* 4 */
font-feature-settings: normal;
/* 5 */
font-variation-settings: normal;
/* 6 */
-webkit-tap-highlight-color: transparent;
/* 7 */
}
/*
@ -125,10 +121,8 @@ strong {
}
/*
1. Use the user's configured `mono` font-family by default.
2. Use the user's configured `mono` font-feature-settings by default.
3. Use the user's configured `mono` font-variation-settings by default.
4. Correct the odd `em` font sizing in all browsers.
1. Use the user's configured `mono` font family by default.
2. Correct the odd `em` font sizing in all browsers.
*/
code,
@ -137,12 +131,8 @@ samp,
pre {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
/* 1 */
font-feature-settings: normal;
/* 2 */
font-variation-settings: normal;
/* 3 */
font-size: 1em;
/* 4 */
/* 2 */
}
/*
@ -1003,7 +993,8 @@ video {
}
.ms-0 {
margin-inline-start: 0px;
-webkit-margin-start: 0px;
margin-inline-start: 0px;
}
.mt-0 {
@ -1549,10 +1540,6 @@ video {
width: 100%;
}
.min-w-full {
min-width: 100%;
}
.min-w-\[250px\] {
min-width: 250px;
}
@ -1561,8 +1548,12 @@ video {
min-width: 300px;
}
.min-w-full {
min-width: 100%;
}
.max-w-0 {
max-width: 0px;
max-width: 0rem;
}
.max-w-2xl {
@ -3790,6 +3781,13 @@ video {
color: rgb(255 255 255 / var(--tw-text-opacity));
}
@media (prefers-color-scheme: dark) {
.dark\:text-gray-600 {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity));
}
}
@media (min-width: 550px) {
.sm\:flex {
display: flex;
@ -4337,11 +4335,4 @@ video {
.xxl\:text-\[70px\] {
font-size: 70px;
}
}
@media (prefers-color-scheme: dark) {
.dark\:text-gray-600 {
--tw-text-opacity: 1;
color: rgb(75 85 99 / var(--tw-text-opacity));
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 KiB

Loading…
Cancel
Save