Skip to content
Open
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: 3 additions & 2 deletions .github/workflows/docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ on:
branches:
- main
tags:
- 'v*.*.*'
- 'v*.*.*'
workflow_dispatch:

env:
IMAGE_NAME: mattfly/obsidian
IMAGE_NAME: obbyirc/obby

jobs:
build:
Expand Down
6 changes: 3 additions & 3 deletions BUILD.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,8 @@ oauth-provider "github" {
### Docker

```sh
docker build -t obsidianirc .
docker run -p 80:80 obsidianirc
docker build -t obby .
docker run -p 80:80 obby
```

#### Building Docker with custom configuration
Expand All @@ -133,7 +133,7 @@ docker build \
--build-arg VITE_DEFAULT_OAUTH_CLIENT_ID="m0obbyircd1234" \
--build-arg VITE_BACKEND_URL=https://api.example.net \
--build-arg VITE_TENOR_API_KEY=your-tenor-key \
-t obsidianirc .
-t obby .
```

### LINUX
Expand Down
2 changes: 1 addition & 1 deletion INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ yay -S obsidianirc
## Docker

```sh
docker run -p 8080:80 mattfly/obsidian:latest
docker run -p 8080:80 obbyirc/obby:latest
```

## Manual build and development
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[![Docker Hub](https://img.shields.io/badge/Docker%20Hub-mattfly%2Fobsidian-blue?logo=docker&style=flat-square)](https://hub.docker.com/r/mattfly/obsidian)
[![Docker Hub](https://img.shields.io/badge/Docker%20Hub-obbyirc%2Fobby-blue?logo=docker&style=flat-square)](https://hub.docker.com/r/obbyirc/obby)
[![Cloudflare Pages](https://img.shields.io/endpoint?url=https://cloudflare-pages-badges.mattf.workers.dev//?projectName=obsidianirc)](https://obsidianirc.pages.dev/)
[![pages-build-deployment](https://github.com/ObsidianIRC/obsidianirc.github.io/actions/workflows/pages/pages-build-deployment/badge.svg)](https://obsidianirc.github.io/)
[![Google Play](https://img.shields.io/badge/Google_Play-Download-414141?logo=google-play)](https://play.google.com/store/apps/details?id=com.obsidianirc.dev)
Expand Down
10 changes: 10 additions & 0 deletions src-tauri/Entitlements.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
</dict>
</plist>
10 changes: 10 additions & 0 deletions src-tauri/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSMicrophoneUsageDescription</key>
<string>ObsidianIRC uses the microphone for voice calls.</string>
<key>NSCameraUsageDescription</key>
<string>ObsidianIRC uses the camera for video calls.</string>
</dict>
</plist>
4 changes: 4 additions & 0 deletions src-tauri/tauri.conf.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
"bundleMediaFramework": true
}
},
"macOS": {
"minimumSystemVersion": "12.0",
"entitlements": "Entitlements.plist"
},
Comment on lines +35 to +38
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

How to configure custom Info.plist in Tauri 2.11.1 bundle.macOS section?

💡 Result:

To configure a custom Info.plist for a Tauri 2.11.1 macOS bundle, you should create a dedicated Info.plist file within your src-tauri directory and reference it in your tauri.conf.json configuration [1][2][3]. 1. Create a custom file: Create a file named Info.plist in your src-tauri folder and add your desired key-value pairs [1][3]. Example src-tauri/Info.plist: NSCameraUsageDescription Request camera access 2. Reference it in tauri.conf.json: Update your tauri.conf.json to point to this file under the bundle.macOS section using the infoPlist field [2]. { "bundle": { "macOS": { "infoPlist": "./Info.plist" } } } Tauri will merge the contents of this file with the default properties generated by the CLI [1]. Be cautious when overriding default values such as the application version or bundle identifier, as this can cause conflicts or unexpected behavior in your application bundle [1]. If you need to support multiple languages, create InfoPlist.strings files within language-specific.lproj directories and include them via the bundle.resources configuration [1]. Additionally, if your custom configuration requires Apple entitlements (such as security permissions), you should manage those separately in an entitlements file and reference it via the entitlements field in the same macOS bundle configuration block [3].

Citations:


Add bundle.macOS.infoPlist reference for camera/mic usage descriptions
You’re only setting entitlements in src-tauri/tauri.conf.json, and Tauri expects a custom Info.plist to be wired via bundle.macOS.infoPlist (e.g., "infoPlist": "./Info.plist") to merge in keys like NSMicrophoneUsageDescription and NSCameraUsageDescription. Without that explicit reference, the permission prompt strings may be missing.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/tauri.conf.json` around lines 35 - 38, The tauri config only sets
"entitlements" under the macOS bundle; add a "infoPlist" reference so Tauri can
merge NSMicrophoneUsageDescription/NSCameraUsageDescription into the app
Info.plist. Update the JSON bundle.macOS object to include an "infoPlist" key
(e.g., "infoPlist": "./Info.plist") alongside "entitlements", and ensure your
Info.plist contains the NSMicrophoneUsageDescription and
NSCameraUsageDescription keys so the permission prompts have proper strings.

"icon": [
"icons/32x32.png",
"icons/128x128.png",
Expand Down
33 changes: 33 additions & 0 deletions src/components/PdfErrorBoundary.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { Component, type ReactNode } from "react";

// Stable reference: react-pdf reloads the document whenever the `options` prop
// is a new object, so this must never be reallocated per render. isEvalSupported
// is disabled to stop pdf.js from running eval() on untrusted document content.
export const PDF_OPTIONS = { isEvalSupported: false } as const;

/**
* react-pdf renders pages through pdf.js passive effects. When the surrounding
* Document unmounts (channel switch, StrictMode re-invoke) the worker transport
* is destroyed, but a still-pending Page effect can call into it and throw
* "Cannot read properties of null (reading 'sendWithPromise')". Without a
* boundary that throw is uncaught and takes down the whole app, so we catch it
* and let the caller fall back to a non-pdf.js renderer.
*/
export class PdfErrorBoundary extends Component<
{ onError: () => void; children: ReactNode },
{ hasError: boolean }
> {
state = { hasError: false };

static getDerivedStateFromError() {
return { hasError: true };
}

componentDidCatch() {
this.props.onError();
}

render() {
return this.state.hasError ? null : this.props.children;
}
}
13 changes: 13 additions & 0 deletions src/components/layout/ChannelMessageList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
SCROLL_TOLERANCE,
useScrollToBottom,
} from "../../hooks/useScrollToBottom";
import { buildMarkdownFromSelection } from "../../lib/chatMarkdownCopy";
import { groupConsecutiveEvents } from "../../lib/eventGrouping";
import ircClient from "../../lib/ircClient";
import useStore from "../../store";
Expand Down Expand Up @@ -413,6 +414,18 @@
className="flex-grow overflow-y-auto overflow-x-hidden flex flex-col bg-discord-dark-200 text-discord-text-normal relative"
// Disable CSS scroll anchoring — it compounds with our useLayoutEffect delta correction causing double-jumps in browser.
style={{ overflowAnchor: "none" }}
onCopy={(e) => {
const root = messagesContainerRef.current;
if (!root) return;
const markdown = buildMarkdownFromSelection(
window.getSelection(),
root,
);
if (markdown !== null) {
e.preventDefault();
e.clipboardData.setData("text/plain", markdown);
}
}}
>
{isLoadingHistory && !isFetchingMore ? (
<div className="flex-grow flex items-center justify-center">
Expand Down Expand Up @@ -521,8 +534,8 @@
);
const previousMessage = channelMessages[originalIndex - 1];
const showHeader =
!previousMessage ||
previousMessage.type !== "message" ||

Check warning on line 538 in src/components/layout/ChannelMessageList.tsx

View workflow job for this annotation

GitHub Actions / Lint and Formating

lint/complexity/useOptionalChain

Change to an optional chain.
previousMessage.userId !== message.userId ||
new Date(message.timestamp).getTime() -
new Date(previousMessage.timestamp).getTime() >
Expand Down
6 changes: 3 additions & 3 deletions src/components/layout/ChatArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2159,9 +2159,9 @@ export const ChatArea: React.FC<{
!isNativeMobile &&
!isCompactInput
? globalSettings.multilineOnShiftEnter
? t`Message #${selectedChannel.name.replace(/^#/, "")} (Shift+Enter for new line)`
: t`Message #${selectedChannel.name.replace(/^#/, "")} (Enter for new line, Shift+Enter to send)`
: t`Message #${selectedChannel.name.replace(/^#/, "")}`
? t`Message ${selectedChannel.name} (Shift+Enter for new line)`
: t`Message ${selectedChannel.name} (Enter for new line, Shift+Enter to send)`
: t`Message ${selectedChannel.name}`
: selectedPrivateChat
? globalSettings.enableMultilineInput &&
!isMobile &&
Expand Down
31 changes: 17 additions & 14 deletions src/components/message/MediaPreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
setVideoPosition,
} from "../../lib/videoPositionCache";
import useStore from "../../store";
import { PDF_OPTIONS, PdfErrorBoundary } from "../PdfErrorBoundary";

const ReactPlayer = lazy(() => import("react-player"));
const LazyDocument = lazy(() =>
Expand Down Expand Up @@ -1021,20 +1022,22 @@ const PdfPreview: React.FC<{
/>
}
>
<LazyDocument
file={url}
options={{ isEvalSupported: false }}
loading={null}
onLoadError={() => setPdfError(true)}
>
<LazyPage
pageNumber={1}
width={PDF_THUMB_W}
canvasBackground="white"
renderTextLayer={false}
renderAnnotationLayer={false}
/>
</LazyDocument>
<PdfErrorBoundary onError={() => setPdfError(true)}>
<LazyDocument
file={url}
options={PDF_OPTIONS}
loading={null}
onLoadError={() => setPdfError(true)}
>
<LazyPage
pageNumber={1}
width={PDF_THUMB_W}
canvasBackground="white"
renderTextLayer={false}
renderAnnotationLayer={false}
/>
</LazyDocument>
</PdfErrorBoundary>
</Suspense>,
);
};
Expand Down
17 changes: 17 additions & 0 deletions src/components/message/MessageItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type * as React from "react";
import { memo, useCallback, useMemo, useRef, useState } from "react";
import { useLongPress } from "../../hooks/useLongPress";
import { useMediaQuery } from "../../hooks/useMediaQuery";
import { formatCopyAuthor } from "../../lib/chatMarkdownCopy";
import { renderWithCustomEmoji, useEmojiResolver } from "../../lib/customEmoji";
import ircClient from "../../lib/ircClient";
import {
Expand Down Expand Up @@ -352,6 +353,19 @@ export const MessageItem = memo((props: MessageItemProps) => {
// message.content is already combined for multiline messages by the IRC client
const messageContent = message.content;

// Author + time + raw markdown source, surfaced as data attributes so the
// chat copy handler (chatMarkdownCopy) can rebuild a markdown transcript that
// preserves authorship and markdown the rendered DOM would otherwise strip.
const copyAuthor = formatCopyAuthor(displayName, message.userId, isSystem);
const copyTime = useMemo(
() =>
new Intl.DateTimeFormat("en-US", {
hour: "2-digit",
minute: "2-digit",
}).format(new Date(message.timestamp)),
[message.timestamp],
);

// draft/custom-emoji: gather pack URLs for this message's channel +
// network in priority order so a channel pack can shadow a network
// shortcode of the same name.
Expand Down Expand Up @@ -668,6 +682,9 @@ export const MessageItem = memo((props: MessageItemProps) => {
<div
ref={messageRowRef}
data-message-id={message.id}
data-md-author={copyAuthor}
data-md-time={copyTime}
data-md-source={messageContent}
className={`px-4 hover:bg-discord-message-hover group relative transition-colors duration-150 ${
showHeader ? "mt-4" : "py-0.5"
}${isHighlighted ? " bg-primary/10 ring-1 ring-primary/30 rounded" : ""}${
Expand Down
32 changes: 18 additions & 14 deletions src/components/ui/MediaViewerModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import { isTauri } from "../../lib/platformUtils";
import useStore, { getChannelMessages } from "../../store";
import type { Message } from "../../types";
import { ResizableSidebar } from "../layout/ResizableSidebar";
import { PDF_OPTIONS, PdfErrorBoundary } from "../PdfErrorBoundary";
import ExternalLinkWarningModal from "./ExternalLinkWarningModal";
import { MediaCommentsSidebar } from "./MediaCommentsSidebar";

Expand Down Expand Up @@ -322,20 +323,23 @@ const PdfModalViewer: React.FC<{ url: string; onRequestOpen: () => void }> = ({
<div className="w-64 h-96 bg-discord-dark-400/50 animate-pulse rounded" />
}
>
<LazyDocument
file={url}
onLoadSuccess={({ numPages: n }) => setNumPages(n)}
onLoadError={() => setUseNativeViewer(true)}
loading={null}
>
<LazyPage
pageNumber={pageNumber}
canvasBackground="white"
renderTextLayer={false}
renderAnnotationLayer={false}
width={pageWidth}
/>
</LazyDocument>
<PdfErrorBoundary onError={() => setUseNativeViewer(true)}>
<LazyDocument
file={url}
options={PDF_OPTIONS}
onLoadSuccess={({ numPages: n }) => setNumPages(n)}
onLoadError={() => setUseNativeViewer(true)}
loading={null}
>
<LazyPage
pageNumber={pageNumber}
canvasBackground="white"
renderTextLayer={false}
renderAnnotationLayer={false}
width={pageWidth}
/>
</LazyDocument>
</PdfErrorBoundary>
</Suspense>
</div>

Expand Down
Loading
Loading